Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >AI写作

c#题例-2025-07-21 07:51:47

当然可以!以下是一道**专家级别**的 C# 程序员逻辑面试题,涉及 **闭包、异步编程、线程安全、委托与事件** 的综合理解,能够很好地考察候选人的底层机制掌握程度和实际开发经验。

---

### 🧠 面试题:闭包陷阱与异步委托执行顺序

**题目:**

请分析以下代码的输出结果,并解释原因。如果有潜在的 bug 或逻辑错误,请指出并提供修复方案。

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

class Program
{
static void Main(string[] args)
{
var actions = new Action[5];

for (int i = 0; i < 5; i++)
{
Task.Delay(100).ContinueWith(_ =>
{
actions[i] = () => Console.WriteLine(i);
});
}

Task.Delay(1000).ContinueWith(_ =>
{
for (int j = 0; j < 5; j++)
{
if (actions[j] != null)
actions[j]();
}
});

Console.ReadLine();
}
}
```

---

### 💡 考察点:

1. **闭包(Closure)陷阱**:在异步 lambda 中捕获循环变量 `i`,会引发常见的闭包延迟绑定问题。
2. **异步执行顺序**:`ContinueWith` 是异步调度的,`i` 可能在 `ContinueWith` 执行前已经改变。
3. **线程安全问题**:多个异步任务修改共享数组 `actions`,可能引发竞争条件。
4. **Action 数组初始化时机**:主线程继续执行,而异步任务是延迟执行的。
5. **延迟执行控制**:`Console.ReadLine()` 阻止主线程退出,确保异步任务有机会执行。

---

### ✅ 预期答案:

#### 1. 输出结果:

输出可能是 **5 个 5 或不一致的值(如 3、4、5 混合)**,甚至某些 `actions[j]` 为 `null`,导致不执行输出。

#### 2. 原因分析:

- **闭包陷阱**:
- `i` 是在 lambda 中被捕获的变量,而不是在 `ContinueWith` 执行时的值。
- 所有 `actions[i] = () => Console.WriteLine(i);` 实际上引用的是同一个变量 `i`,它在循环结束后变为 5。

- **异步调度不确定性**:
- `ContinueWith` 中的代码是异步执行的,主线程不会等待它们完成。
- `for` 循环结束后,`i` 已经变成 5,而 `ContinueWith` 内部的 `i` 会反映这个值。

- **线程安全问题**:
- 多个任务同时修改共享数组 `actions`,没有同步机制,可能引发竞争条件。

#### 3. 修复方案:

- **捕获变量副本**:
- 在循环内创建局部变量 `int temp = i;`,并在 `ContinueWith` 中使用该变量。

- **确保线程安全**:
- 可以使用锁机制或使用线程安全集合。

- **等待异步任务完成**(可选):
- 可以使用 `Task.WaitAll` 或 `ManualResetEvent` 来确保主函数等待异步任务完成。

#### ✅ 修复后的代码示例:

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

class Program
{
static void Main(string[] args)
{
var actions = new Action[5];

for (int i = 0; i < 5; i++)
{
int temp = i; // 捕获副本
Task.Delay(100).ContinueWith(_ =>
{
actions[temp] = () => Console.WriteLine(temp);
});
}

Task.Delay(1000).ContinueWith(_ =>
{
for (int j = 0; j < 5; j++)
{
if (actions[j] != null)
actions[j]();
}
});

Console.ReadLine(); // 确保主线程不退出
}
}
```

#### ✅ 输出结果:

```
0
1
2
3
4
```

---

### 🧩 进阶追问(可选):

1. 如何保证 `actions` 数组的线程安全赋值?
2. 如果不使用 `Console.ReadLine()`,如何保证主线程等待所有异步任务完成?
3. 如果要按顺序执行这些异步任务,你会如何修改代码?

---

如果你需要我再出一道不同方向的专家级题目(例如:泛型、反射、表达式树、性能优化、GC机制等),也可以告诉我!

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

上一篇: c#题例-2025-07-21 13:25:18

下一篇: c#题例-2025-07-21 02:18:34

精华推荐