Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >AI写作

c#题例-2025-08-01 15:58:35

当然可以!下面是一道**专家级别**的 C# 逻辑面试题,涉及 **委托、事件、异步编程、闭包、线程安全**等多个高级概念,适合考察资深 C# 程序员对语言机制和多线程编程的深入理解。

---

### 🧠 面试题:异步委托与闭包陷阱

#### 问题描述:

请看以下 C# 代码:

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

class Program
{
static void Main()
{
for (var i = 0; i < 5; i++)
{
Task.Run(() =>
{
Console.WriteLine(i);
});
}

Console.ReadLine(); // 等待所有任务完成
}
}
```

**问题1:** 这段代码的输出是什么?为什么?

**问题2(进阶):** 如何修改代码,使得输出为 `0, 1, 2, 3, 4`(顺序不重要,但每个数字只出现一次)?

**问题3(挑战):** 如果将 `Task.Run` 替换为 `Task.Factory.StartNew`,是否会影响结果?为什么?

**问题4(扩展):** 如果在 `Task.Run` 中调用的是 `async/await` 方法,例如:

```csharp
Task.Run(async () =>
{
await Task.Delay(100);
Console.WriteLine(i);
});
```

这会影响输出结果吗?为什么?

---

### ✅ 考察点解析:

1. **闭包陷阱(Closure Issue)**:
- 原始代码中,所有任务捕获的是同一个变量 `i` 的引用,而不是其值的副本。
- 所有任务执行时,循环已经完成,`i` 的值是 `5`,所以输出都是 `5`。

2. **如何修复闭包陷阱**:
- 在循环内部创建一个局部副本:`int temp = i;` 然后在 `Task.Run` 中使用 `temp`。
- 或者直接将 `i` 作为参数传入委托。

3. **Task.Run vs Task.Factory.StartNew**:
- `Task.Run` 是 `Task.Factory.StartNew` 的简化版本,使用默认配置(如默认调度器、默认任务创建选项)。
- 通常不会影响结果,除非你显式修改了调度器或行为(如 TaskScheduler.FromCurrentSynchronizationContext)。

4. **async/await 对闭包的影响**:
- 不会改变闭包行为,仍然捕获的是变量 `i` 的引用。
- 只是延迟了输出的时间,但最终输出仍然是 `5, 5, 5, 5, 5`。

---

### 💡 延伸讨论(面试官可选):

- 如何使用 `ValueTask` 提升性能?
- 如何使用 `IAsyncEnumerable` 或 `Channel` 实现线程安全的数据流?
- 如何使用 `ConcurrentBag` 或锁机制确保线程安全?
- 使用 `async void` 的风险是什么?

---

如果你能流畅地回答上述问题,说明你对 C# 的异步编程模型、闭包机制、委托和线程安全都有较深入的理解,已经达到了高级或专家级 C# 程序员的水平。

需要我继续出类似的题目吗?

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

上一篇: c#题例-2025-08-01 21:31:49

下一篇: c#题例-2025-08-01 10:25:23

精华推荐