Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >AI写作

c#题例-2025-06-25 22:51:51

当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,涉及到 **委托、事件、异步编程、闭包捕获、线程安全与死锁预防**等多个高级知识点:

---

### 🧠 面试题:理解异步委托链与捕获变量行为

#### 问题描述:

你有以下 C# 控制台应用程序代码片段:

```csharp
using System;
using System.Threading.Tasks;

class Program
{
static async Task Main(string[] args)
{
Action action = null;

for (int i = 0; i < 5; i++)
{
int captured = i;

await Task.Delay(100); // 模拟异步操作

action += () => Console.Write(captured + " ");
}

Parallel.Invoke(action.GetInvocationList());
}
}
```

#### 问题:

1. 这段代码会输出什么?请解释原因。
2. 如果将 `Parallel.Invoke` 替换为同步调用 `action(0)`(虽然类型不匹配,但假设你遍历调用每个委托),输出会不同吗?
3. 如何修改这段代码以确保输出是确定的,并且按照预期打印出 0 到 4 的顺序?

---

### 🔍 参考答案与解析:

#### 1. 输出可能不确定,例如:
由于使用了 `Parallel.Invoke` 并发执行多个委托,而这些委托捕获的是局部变量 `captured`,其值在各线程中可能被优化或共享访问导致竞争条件。

- 在某些运行中,可能会看到类似 `0 1 2 3 4`,但也可能出现重复数字如 `4 4 4 4 4`。
- 原因在于:虽然 `captured = i` 是一个“固定”的局部变量,但在多线程并发执行下,C# 编译器生成的闭包类字段可能没有足够的内存屏障保证可见性一致性。

#### 2. 同步调用 vs 异步并发调用的区别:

- 如果改为同步逐个调用,则每个委托是在主线程中按顺序执行,此时输出是确定的:`0 1 2 3 4`。
- 因为每个委托捕获的 `captured` 是循环中的副本,不会出现并发读写冲突。

#### 3. 修改建议(确保线程安全和确定性输出):

```csharp
Action action = null;

for (int i = 0; i < 5; i++)
{
int captured = i;

await Task.Delay(100);

action += (dummy) => Console.Write(captured + " ");
}

// 改为同步调用:
foreach (Action handler in action.GetInvocationList())
{
handler(0); // 忽略参数,只是为了保持委托签名一致
}
```

或者更佳方式:避免并发调用委托链,改用顺序调用或显式创建任务并等待完成。

---

### 💡 考察点总结:

- **闭包捕获机制**
- **委托链(multicast delegate)的行为**
- **Parallel.Invoke 的线程调度特性**
- **异步上下文(async/await)对捕获变量的影响**
- **线程安全与竞态条件处理**

---

如果你需要我再出一道类似的高阶题目,比如涉及 **表达式树、动态代理、泛型约束、协变逆变、反射 Emit 或者 unsafe 操作等**,也可以告诉我!

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: c#题例-2025-06-26 04:25:06

下一篇: c#题例-2025-06-25 17:18:37

精华推荐