c#题例-2025-07-26 20:31:59
日期: 2025-07-26 分类: AI写作 11次阅读
当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,它结合了 **委托、事件、异步编程、闭包捕获、线程安全** 等多个高级概念,考察候选人对 C# 语言底层机制和多线程编程的理解。
---
### 🧠 面试题:事件订阅与异步执行中的闭包陷阱
**题目描述:**
请分析以下代码的输出结果,并解释原因。此外,请指出其中潜在的线程安全问题,并提供一个线程安全的修改方案。
```csharp
using System;
using System.Threading.Tasks;
public class EventPublisher
{
public event EventHandler
public async Task ProcessNumbersAsync()
{
var numbers = new int[] { 1, 2, 3, 4, 5 };
foreach (var number in numbers)
{
await Task.Delay(10); // 模拟异步操作
int result = number * number;
NumberProcessed?.Invoke(this, result);
}
}
}
public class Program
{
public static async Task Main()
{
var publisher = new EventPublisher();
for (int i = 0; i < 3; i++)
{
int capture = i;
publisher.NumberProcessed += async (sender, value) =>
{
await Task.Delay(5); // 模拟异步处理
Console.WriteLine($"Subscriber {capture}: Received {value}");
};
}
await publisher.ProcessNumbersAsync();
}
}
```
---
### ❓问题:
1. **输出顺序是否可预测?为什么?**
2. **是否每个 `capture` 变量都正确地被捕获?是否存在闭包陷阱?**
3. **`NumberProcessed` 事件的调用是否线程安全?如果不安全,如何改进?**
4. **如果在 `Main` 方法中添加 `Console.ReadKey()`,会有什么影响?**
---
### 🧩 考察点解析:
| 考察点 | 描述 |
|--------|------|
| **闭包捕获** | 是否理解 `capture` 在循环中被正确捕获,而不是被异步 lambda 捕获循环变量 `i`(闭包陷阱) |
| **异步事件处理** | 是否理解事件处理函数中使用 `async void` 的潜在问题(如异常无法捕获) |
| **线程安全** | 是否意识到事件多播委托不是线程安全的,多个线程同时触发事件可能导致异常 |
| **事件调用模式** | 是否知道如何安全地调用事件(如使用 `?.Invoke()` 或复制委托) |
| **TPL 与 async/await 的理解** | 是否理解任务调度、延迟执行和输出顺序的不确定性 |
---
### ✅ 推荐答案要点:
#### 1. 输出顺序不可预测
因为每个事件处理函数中都有 `await Task.Delay(5)`,它们在不同的任务中异步执行,因此输出顺序是不确定的。
#### 2. `capture` 是安全的
由于在循环中将 `i` 赋值给局部变量 `capture`,因此每个 lambda 捕获的是不同的变量副本,没有闭包陷阱。如果直接使用 `i`,就会出现问题。
#### 3. 事件调用不是线程安全的
`NumberProcessed?.Invoke()` 在多线程环境下可能因委托被修改而抛出异常。推荐使用复制委托的方式:
```csharp
var handler = NumberProcessed;
handler?.Invoke(this, result);
```
#### 4. `Console.ReadKey()` 的影响
如果 `Main` 方法结束得太快,程序会退出,导致异步任务被中断。加上 `ReadKey()` 可以防止主线程提前退出。
---
### 🛠 修改建议(线程安全版本):
```csharp
public async Task ProcessNumbersAsync()
{
var numbers = new int[] { 1, 2, 3, 4, 5 };
foreach (var number in numbers)
{
await Task.Delay(10);
int result = number * number;
var handler = NumberProcessed;
if (handler != null)
{
foreach (var invocation in handler.GetInvocationList())
{
// 可选:使用 Task.Run 并行处理每个订阅者
_ = Task.Run(() => ((EventHandler
}
}
}
}
```
---
如果你能正确分析这段代码并给出改进方案,说明你已经具备了 C# 高级开发者的水平!
需要我再出一道类似的题目吗?或者想看看这道题的标准答案完整实现?
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐