c#题例-2025-08-09 15:51:56
日期: 2025-08-09 分类: AI写作 12次阅读
当然可以!下面是一道**专家级别**的 C# 逻辑面试题,涵盖了 **委托、泛型、异步编程、闭包、线程安全** 等多个高级知识点,适合考察资深 C# 开发者的综合能力:
---
### 🧠 面试题:异步委托与闭包陷阱
**题目描述:**
请看以下代码片段,并回答问题。
```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).Wait(); // 模拟耗时操作
int counter = i * 2;
actions[i] = async () =>
{
await Task.Delay(50); // 模拟异步操作
Console.WriteLine($"Value: {counter}");
};
}
Parallel.For(0, 5, i =>
{
actions[i]?.Invoke();
});
Console.ReadLine();
}
}
```
---
### ❓问题一:这段代码的输出是否确定?为什么?
---
### ❓问题二:是否存在线程安全问题?请说明理由。
---
### ❓问题三:如果要确保输出顺序为:
```
Value: 0
Value: 2
Value: 4
Value: 6
Value: 8
```
**你应该如何修改代码?**
---
### ❓问题四(Bonus):如果使用 `async/await` 的方式重写 `Parallel.For` 部分,你会如何实现?是否应该这么做?为什么?
---
### ✅ 参考答案要点:
---
#### ✅ 回答一:输出不确定
- **原因:闭包陷阱(Closure Capturing)**
- 在 `for` 循环中,变量 `counter` 是在循环体中定义的局部变量。
- 但是,委托捕获的是 `counter` 的**引用**,而不是值。
- 由于 `Parallel.For` 是并行执行的,`counter` 的值可能已经被后续循环修改,导致输出混乱。
- 尽管 `counter = i * 2` 是局部变量,但由于在 `async` lambda 中被异步访问,仍可能引发竞态条件。
---
#### ✅ 回答二:存在线程安全问题
- `Parallel.For` 并行调用 `actions[i]()`,每个 `Action` 都是异步的。
- 虽然 `Console.WriteLine` 本身是线程安全的,但**多个异步方法同时执行**,导致输出顺序不可预测。
- 更重要的是,`counter` 被多个异步委托共享,如果其值被后续循环修改,则输出不一致。
---
#### ✅ 回答三:如何修正
**解决方案:捕获变量的当前值,避免闭包共享问题**
```csharp
for (int i = 0; i < 5; i++)
{
Task.Delay(100).Wait(); // 模拟耗时操作
int counter = i * 2;
int localCounter = counter; // 创建副本,避免闭包引用共享
actions[i] = async () =>
{
await Task.Delay(50); // 模拟异步操作
Console.WriteLine($"Value: {localCounter}");
};
}
```
或者:
```csharp
actions[i] = async () =>
{
int value = counter; // 立即捕获当前值
await Task.Delay(50);
Console.WriteLine($"Value: {value}");
};
```
> 这样可以确保每个委托捕获的是**当前迭代的副本**,而不是引用。
---
#### ✅ 回答四(Bonus):如何使用 async/await 替代 Parallel.For?
可以使用 `Task.Run` 或者 `Task.WhenAll` 来处理:
```csharp
await Task.WhenAll(actions
.Where(a => a != null)
.Select(a => Task.Run(a)));
```
> ⚠️ 但需要注意:
- `Parallel.For` 是同步并行,而 `async/await` 是异步并发,两者语义不同。
- 如果目标是并发执行异步操作,使用 `Task.WhenAll` 是更合适的方式。
- 不应盲目替换,需根据业务场景选择同步并行还是异步并发。
---
### 🧩 总结:这道题考察的知识点包括:
| 技术点 | 说明 |
|----------------------|------|
| 委托与闭包 | Lambda 表达式捕获变量的方式 |
| 异步编程 | async/await 的使用与调度 |
| 线程安全与竞态条件 | 多线程下变量访问问题 |
| Parallel.For 与 Task | 并行与异步的区别 |
| C# 变量捕获机制 | 值类型 vs 引用捕获 |
---
如果你能完整回答这道题,说明你已经具备**专家级 C# 编程能力**!需要我继续出类似的题目吗?
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐