Young87

SmartCat's Blog

So happy to code my life!

游戏开发交流QQ群号60398951

当前位置:首页 >AI写作

c#题例-2025-06-24 21:18:48

当然可以!以下是一道**专家级别**的 C# 逻辑面试题,涵盖了 **委托、事件、异步编程、闭包陷阱、线程安全和死锁预防** 等多个高级概念:

---

### 🧠 面试题:深入理解委托、事件与异步生命周期

#### 背景描述:
你正在开发一个实时数据采集系统。系统中有一个 `Sensor` 类,用于模拟传感器不断读取数据;还有一个 `DataProcessor` 类,用于订阅这些数据并进行处理。

#### 示例代码如下:

```csharp
public class SensorEventArgs : EventArgs
{
public double Value { get; set; }
}

public class Sensor
{
public event EventHandler DataReady;

public async Task StartReadingAsync(CancellationToken token)
{
var rand = new Random();
while (!token.IsCancellationRequested)
{
await Task.Delay(500, token); // 模拟每500毫秒采集一次
OnDataReady(rand.NextDouble() * 100);
}
}

protected virtual void OnDataReady(double value)
{
DataReady?.Invoke(this, new SensorEventArgs { Value = value });
}
}
```

```csharp
public class DataProcessor : IDisposable
{
private readonly Sensor _sensor;
private readonly Action _logAction;

public DataProcessor(Sensor sensor, Action logAction)
{
_sensor = sensor;
_logAction = logAction;

_sensor.DataReady += HandleDataReady;
}

private void HandleDataReady(object sender, SensorEventArgs e)
{
Task.Run(() =>
{
// 模拟耗时处理
Thread.Sleep(200);
_logAction(e.Value);
});
}

public void Dispose()
{
_sensor.DataReady -= HandleDataReady;
}
}
```

#### 主程序如下:

```csharp
class Program
{
static async Task Main(string[] args)
{
var cts = new CancellationTokenSource();
var sensor = new Sensor();

var processor1 = new DataProcessor(sensor, val => Console.WriteLine($"Processor1: {val}"));
var processor2 = new DataProcessor(sensor, val => Console.WriteLine($"Processor2: {val}"));

var readingTask = sensor.StartReadingAsync(cts.Token);

Console.WriteLine("Press any key to exit...");
Console.ReadKey();

cts.Cancel();
await readingTask;
}
}
```

---

### ❓问题一(中级):
上面的代码中存在**内存泄漏风险**,你能指出原因吗?应该如何修复?

---

### ❓问题二(高级):
在 `DataProcessor` 的 `HandleDataReady` 方法中使用了 `Task.Run` 来处理日志输出。这样做是否合理?如果传感器频率提高到每 100 毫秒一次,可能会出现什么问题?如何优化?

---

### ❓问题三(专家级):
假设你将 `DataProcessor` 注册为依赖注入服务(Singleton),并且传入了一个捕获当前作用域变量的 `Action`(如捕获某个控制器的状态)。在这种情况下,会出现什么潜在的问题?如何设计才能避免此类问题?

---

### ✅参考答案要点:

#### 回答一(内存泄漏):
- `DataProcessor` 订阅了 `sensor.DataReady` 事件,但如果未正确调用 `Dispose()`,即使 `processor1` 或 `processor2` 不再使用,它们也不会被垃圾回收。
- 即使调用了 `Dispose()`,由于 `EventHandler` 是强引用,在某些场景下仍可能造成泄漏。
- 推荐使用 **弱引用事件模式(Weak Event Pattern)** 或引入 `WeakReference` 手动管理订阅者。

#### 回答二(并发与资源竞争):
- 使用 `Task.Run` 可以避免阻塞主线程,但在高频触发下会导致任务队列堆积,增加内存消耗。
- 如果处理函数执行时间 > 触发间隔,会形成“任务雪崩”。
- 建议改用 `DataflowBlock` 或 `Channel` + `Consumer` 模式控制背压。

#### 回答三(闭包与生命周期):
- 如果传入的 `Action` 捕获了外部对象(例如 MVC 控制器或页面状态),可能导致该对象无法释放。
- 在 Singleton 生命周期中持有短生命周期对象的引用,会造成内存泄漏。
- 解决方案包括:
- 使用局部静态方法或分离逻辑;
- 显式解耦,使用工厂模式创建处理器;
- 引入 `IOptionsMonitor` 或 `IServiceScopeFactory` 动态获取作用域服务。

---

### 🎯附加考察点:
- 对 **CancellationToken 的正确使用**
- 对 **async/await 和线程池调度的理解**
- 是否了解 **.NET 内存管理机制**
- 是否有实际开发中解决过类似问题的经验

---

如果你需要更多这类题目(比如关于表达式树、IL emit、反射性能优化等),我也可以继续提供!

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

上一篇: c#题例-2025-06-25 02:51:57

下一篇: c#题例-2025-06-24 15:45:25

精华推荐