BlockingCollection实现生产者消费者模式

功能特性

BlockingCollection 是一个线程安全集合类,其内封装实现 IProducerConsumerCollection 的各种集合类型,如 ConcurrentQueue、ConcurrentStack 等。主要有以下特性:

  1. 可以快速实现生产者-消费者模式;
  2. 支持多个生产者(Producer)并发添加元素,多个消费者(Consumer)并发取出元素;
  3. 支持设置最大容量,设置最大容量后,生产者会在集合满时阻塞,直到有空间可用
  4. 当集合为空时,消费者可以阻塞,等待新元素加入;
  5. 当集合满时,生产者可以等待空间释放(可选)
  6. 使用GetConsumingEnumerable()返回一个可枚举对象,用于在消费者中遍历集合中的元素。它是一个无限枚举器,只有当集合为空 且 CompleteAdding() 被调用 后,才会退出循环,否则将会阻塞等待;
  7. 使用取消标记执行取消操作。

使用场景

场景描述
多线程任务调度多个后台线程从集合中获取任务执行
队列式处理如日志处理、消息队列等
批量数据处理支持多个生产者并发写入,多个消费者并发读取
异步流处理结合 GetConsumingEnumerable() 实现持续消费

常用方法

功能方法
添加元素(生产者方法)Add(item) / TryAdd(…)
获取元素(消费者方法)Take() / TryTake(…)
遍历消费(遍历消费)GetConsumingEnumerable()
标记完成CompleteAdding()
是否已完成IsCompleted
集合是否为空IsEmpty
最大容量通过构造函数传参控制

构造方式

// 使用默认内部队列(ConcurrentQueue<T>)
BlockingCollection<string> collection = new BlockingCollection<string>();

// 指定最大容量(有界集合)
BlockingCollection<string> boundedCollection = new BlockingCollection<string>(new ConcurrentQueue<string>(), 10);

// 使用其他集合类型(例如 ConcurrentStack)
BlockingCollection<int> stackBasedCollection = new BlockingCollection<int>(new ConcurrentStack<int>());

生产者消费者模式示例

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        var collection = new BlockingCollection<string>();

        // 消费者任务
        Task consumerTask = Task.Run(() =>
        {
            foreach (var item in collection.GetConsumingEnumerable())
            {
                Console.WriteLine($"消费: {item}");
            }
            Console.WriteLine("消费结束");
        });

        // 生产者任务
        Task producerTask = Task.Run(() =>
        {
            for (int i = 1; i <= 5; i++)
            {
                collection.Add($"Item{i}");
                Console.WriteLine($"生产: Item{i}");
                Task.Delay(500).Wait();
            }

            collection.CompleteAdding(); // 完成生产
        });

        Task.WaitAll(consumerTask, producerTask);
        Console.WriteLine("程序结束");
    }
}

说明:只有调用collection.CompleteAdding(),且collection为空时,consumerTask才可能终止;否则,因为collection.GetConsumingEnumerable()的无限迭代,consumerTask将无法终止。如果某线程确定需要持续后台运行,则可不调用collection.CompleteAdding()。

消费者退出的两种方式

  1. 如上代码,在生产者完成添加后,主动调用 CompleteAdding(),当消费者消费所有内容后,则会自动退出。

  2. 结合 CancellationToken 来强制退出循环,适用于需要提前终止的情况

var cts = new CancellationTokenSource();
Task consumerTask = Task.Run(() =>
{
    foreach (var item in collection.GetConsumingEnumerable(cts.Token))
    {
        Console.WriteLine($"消费: {item}");
    }
    Console.WriteLine("消费者已退出");
});

在之后,调用cts.Cancel()主动取消消费者。此时会抛出 OperationCanceledException,可以捕获它来更加灵活地控制退出。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值