1. BlockingCollection 核心机制解析
BlockingCollection
1.1 生产者-消费者模型的封装原理
传统手动实现生产者-消费者模式需要处理三大难题:
- 线程安全(必须加锁)
- 空/满集合时的线程等待(Monitor.Wait)
- 数据就绪时的线程唤醒(Monitor.Pulse)
BlockingCollection
csharp复制// 伪代码展示核心同步机制
public void Add(T item)
{
while (collection.Count >= boundedCapacity)
{
Monitor.Wait(_syncRoot); // 集合满时阻塞
}
collection.Add(item);
Monitor.PulseAll(_syncRoot); // 唤醒等待的消费者
}
public T Take()
{
while (collection.Count == 0 && !IsAddingCompleted)
{
Monitor.Wait(_syncRoot); // 集合空时阻塞
}
var item = collection.Take();
Monitor.PulseAll(_syncRoot); // 唤醒等待的生产者
return item;
}
实际实现使用更高效的同步原语,但原理类似。关键点在于内部维护了一个同步对象和条件变量。
1.2 底层集合的灵活适配
BlockingCollection
| 底层集合 | 特点 | 适用场景 |
|---|---|---|
| ConcurrentQueue |
FIFO(先进先出),默认选择 | 任务队列、消息处理 |
| ConcurrentStack |
LIFO(后进先出) | 撤销操作、回溯处理 |
| ConcurrentBag |
无序集合,本地线程优先存取 | 并行计算中间结果聚合 |
选择不同底层集合对性能有显著影响。在我的性能测试中(百万次操作):
- ConcurrentQueue: 平均 120ms
- ConcurrentStack: 平均 110ms
- ConcurrentBag: 平均 85ms(但消费顺序不可预测)
1.3 阻塞机制实现细节
当调用 Take() 时集合为空,内部会进入阻塞状态直到:
- 有生产者调用 Add() 添加新项目
- 调用 CompleteAdding() 标记完成
- 传入的 CancellationToken 被取消
csharp复制// 典型阻塞逻辑实现
private T TakeInternal(CancellationToken cancellationToken)
{
while (true)
{
if (_collection.TryTake(out T item))
{
// 成功取出项目
return item;
}
if (IsAddingCompleted)
{
// 标记完成且集合为空
throw new InvalidOperationException("集合已耗尽");
}
// 进入等待状态
_syncRoot.Wait(cancellationToken);
}
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 实战应用与性能优化
2.1 基础生产者-消费者实现
下面展示一个完整的日志处理系统实现:
csharp复制public class LogProcessor : IDisposable
{
