Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >AI写作

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

当然可以!下面是一道**专家级别**的 **C# 逻辑面试题**,它不仅考察候选人对 C# 语言特性的掌握,还涉及 **委托、事件、异步编程、闭包、线程安全、内存管理**等多个高级知识点。

---

### 🧠 面试题:多线程下的事件订阅陷阱

你正在开发一个多线程应用程序,其中有一个事件发布者类 `EventPublisher` 和多个订阅者。下面是简化版的代码:

```csharp
public class EventPublisher
{
public event EventHandler MyEvent;

public void TriggerEvent()
{
MyEvent?.Invoke(this, EventArgs.Empty);
}
}

public class Subscriber
{
private int _id;

public Subscriber(int id, EventPublisher publisher)
{
_id = id;
publisher.MyEvent += HandleEvent;
}

private async void HandleEvent(object sender, EventArgs e)
{
await Task.Delay(100); // 模拟异步操作
Console.WriteLine($"Subscriber {_id} handled the event.");
}
}
```

在主程序中,你这样使用:

```csharp
var publisher = new EventPublisher();

Parallel.For(0, 100, i =>
{
var sub = new Subscriber(i, publisher);
});

publisher.TriggerEvent();
Console.ReadLine();
```

---

### ❓问题:

1. **这段代码存在什么潜在问题?**
2. **为什么某些订阅者可能没有收到事件?**
3. **如何改进代码以确保所有订阅者都能正确接收到事件?**
4. **如果将 `HandleEvent` 中的 `async void` 改为 `async Task`,是否会影响行为?为什么?**
5. **从线程安全和资源管理的角度看,这段代码还存在哪些隐患?**

---

### ✅ 考察点解析:

| 考点 | 说明 |
|------|------|
| `Parallel.For` 与线程安全 | 多线程中操作事件订阅是否安全 |
| `async void` 的陷阱 | 容易导致异常无法捕获、生命周期管理困难 |
| 垃圾回收与事件订阅 | 如果订阅者是临时对象,但未取消订阅,可能造成内存泄漏 |
| `event` 的线程安全性 | `EventHandler` 多线程调用是否安全(`MyEvent?.Invoke`) |
| `async/await` 与 UI 线程上下文 | 如果在 UI 线程中运行,`async void` 可能引发上下文问题 |

---

### 💡 参考答案提示:

1. **潜在问题:**
- `Parallel.For` 中创建 `Subscriber` 实例,构造函数中注册事件。
- 这些实例没有被引用,可能被 GC 回收,但事件仍被订阅,导致 `WeakReference` 陷阱或无效引用。
- `MyEvent` 是普通事件,在多线程下注册和触发不安全。

2. **某些订阅者未收到事件的原因:**
- 如果 `Subscriber` 实例被提前回收,GC 可能已经回收了它们,但由于未取消订阅,其 `HandleEvent` 仍可能被调用(如果事件触发时对象还活着)。
- `Parallel.For` 是并行执行,`TriggerEvent` 可能在某些订阅尚未完成时就执行了。

3. **如何改进:**
- 使用 `WeakReference` 或 `WeakEventManager`(WPF 中)来避免内存泄漏。
- 使用线程安全的事件容器,如 `ConcurrentDictionary`。
- 使用 `CancellationToken` 控制生命周期。
- 避免在构造函数中直接订阅事件,改为显式调用。

4. **`async void` 改为 `async Task`:**
- 会编译错误,因为事件处理函数必须返回 `void`。
- `async void` 是“fire and forget”,异常无法捕获,容易导致崩溃。
- 更好的做法是使用 `Task.Run(() => ...)` 包裹同步逻辑。

5. **其他隐患:**
- 没有取消订阅机制,导致内存泄漏。
- `Parallel.For` 中创建的对象未被保存,容易被回收。
- `EventHandler` 多线程访问未同步。

---

### 🧩 扩展挑战(可选):

请实现一个线程安全、支持异步处理、并能自动取消订阅的事件系统,适用于高并发环境。

---

如果你需要,我也可以提供一个完整的改进版本代码。是否需要?

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

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

下一篇: c#题例-2025-08-01 04:51:47

精华推荐