c#题例-2025-07-18 16:19:09
日期: 2025-07-18 分类: AI写作 15次阅读
当然可以!以下是一道**专家级别**的 C# 程序员逻辑面试题,涉及 **委托、事件、异步编程、闭包、线程安全和设计模式** 的综合理解:
---
### 🧠 面试题:异步事件总线与线程安全陷阱
你正在开发一个跨平台应用程序,决定使用一个简单的**事件总线(Event Bus)**来实现模块间通信。你设计了如下事件总线类:
```csharp
public class EventBus
{
private event EventHandler
public void Subscribe(EventHandler
{
SomeEvent += handler;
}
public void Unsubscribe(EventHandler
{
SomeEvent -= handler;
}
public async Task RaiseEventAsync()
{
await Task.Run(() =>
{
SomeEvent?.Invoke(this, EventArgs.Empty);
});
}
}
```
然后你在多个模块中使用它:
```csharp
class ModuleA
{
public void OnSomeEvent(object sender, EventArgs args)
{
Console.WriteLine($"ModuleA: {Task.CurrentId}");
}
}
class ModuleB
{
public void OnSomeEvent(object sender, EventArgs args)
{
Console.WriteLine($"ModuleB: {Task.CurrentId}");
}
}
```
主程序如下:
```csharp
var bus = new EventBus();
var a = new ModuleA();
var b = new ModuleB();
bus.Subscribe(a.OnSomeEvent);
bus.Subscribe(b.OnSomeEvent);
await bus.RaiseEventAsync();
bus.Unsubscribe(a.OnSomeEvent);
bus.Unsubscribe(b.OnSomeEvent);
await Task.Delay(100); // 确保事件处理完成
```
---
### ❓问题如下:
1. **存在什么潜在的线程安全问题?**
2. **如果 `RaiseEventAsync` 中的事件处理方法执行时间较长,是否会影响主线程?为什么?**
3. **在 `Task.Run` 中调用 `SomeEvent?.Invoke(...)` 是否合理?如果不合理,如何改进?**
4. **如果你希望事件订阅者在 UI 线程上执行(比如 WPF 或 WinForms),应该如何修改代码?**
5. **假设你希望支持异步事件处理器(即 `async/await` 的事件处理方法),该如何修改 `EventHandler` 的定义?**
---
### ✅ 期望考察点与参考答案思路:
#### 1. 潜在线程安全问题:
- `SomeEvent` 是一个普通的 C# 事件,其内部使用的是多播委托(MulticastDelegate),但**+= 和 -= 是非线程安全的操作**。
- 如果多个线程同时调用 `Subscribe` 和 `Unsubscribe`,可能导致事件订阅不一致或丢失。
- **改进方法**:使用 `Interlocked` 或者封装线程安全的订阅机制,或使用 `ReaderWriterLockSlim`。
#### 2. 长时间事件处理是否影响主线程:
- 不影响主线程,因为 `RaiseEventAsync` 使用了 `Task.Run`,事件处理是在**后台线程**中执行的。
- 但如果 UI 线程需要更新界面,必须切换回 UI 线程上下文。
#### 3. `Invoke` 是否合理?
- 不合理。`EventHandler` 通常设计用于 UI 线程或同步上下文,将其在后台线程直接调用可能引发异常(如 UI 控件访问)。
- 更合理的方式是:将事件触发逻辑封装为 `Invoke` 的安全方式,或使用 `SynchronizationContext` 来调度到正确的上下文。
#### 4. 事件订阅者在 UI 线程执行:
- 可以在 `EventBus` 中捕获 `SynchronizationContext.Current`(在 UI 线程中创建时)。
- 修改 `RaiseEventAsync`,使用该上下文调用事件处理函数:
```csharp
private readonly SynchronizationContext _context;
public EventBus()
{
_context = SynchronizationContext.Current;
}
public async Task RaiseEventAsync()
{
var handlers = SomeEvent.GetInvocationList()
.Cast
foreach (var handler in handlers)
{
if (_context != null)
{
_context.Post(_ => handler(this, EventArgs.Empty), null);
}
else
{
await Task.Run(() => handler(this, EventArgs.Empty));
}
}
}
```
#### 5. 支持异步事件处理器:
- 将 `EventHandler` 替换为自定义的异步委托:
```csharp
public delegate Task AsyncEventHandler
private event AsyncEventHandler
public void Subscribe(AsyncEventHandler
{
SomeEvent += handler;
}
public void Unsubscribe(AsyncEventHandler
{
SomeEvent -= handler;
}
public async Task RaiseEventAsync()
{
var handlers = SomeEvent.GetInvocationList()
.Cast
foreach (var handler in handlers)
{
await handler(this, EventArgs.Empty);
}
}
```
---
### 🔚 总结:
这道题考察了候选人对以下方面的综合掌握:
- 委托与事件的底层机制
- 异步编程(Task、SynchronizationContext)
- 线程安全问题
- 多播委托的调用与异常处理
- UI 线程交互
- 设计可扩展的事件总线结构
---
如果你需要更多类似难度的题目(如涉及表达式树、IL Emit、AOP、性能优化等),我也可以继续出题!
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐