.NET 中实现生产者-消费者模型,BlockingCollection<T> 和 Channel<T>使用示例

一、方案对比:不同线程安全集合的适用场景

二、推荐方案及示例代码

方案 1:使用 BlockingCollection(同步模型)

csharp 复制代码
public class QueueDemo
{
    private readonly BlockingCollection<int> _blockingCollection = new BlockingCollection<int>();

    private readonly CancellationTokenSource _cts = new CancellationTokenSource();

    public QueueDemo()
    {
    }

    // 生产者方法
    public void ProduceData()
    {
        Task.Run(() =>
        {
            var rnd = new Random();
            while (!_cts.IsCancellationRequested)
            {
                var item = rnd.Next(1, 100);

                _blockingCollection.Add(item);      // 触发消费者唤醒

                Console.WriteLine($"Produced1: {item}");
                Thread.Sleep(500); // 模拟生产间隔

                //if(DateTime.Now > Convert.ToDateTime("2025-02-05 16:28:00")) break;
            }

            _blockingCollection.CompleteAdding(); // 结束消费
        });
    }

    // 消费者方法
    public void ConsumeData()
    {
        // 方式1:阻塞消费(推荐)
        Task.Run(() =>
        {
            try
            {
                Thread.Sleep(1000);
                // 使用阻塞方式消费(自动处理空队列等待)
                foreach (var item in _blockingCollection.GetConsumingEnumerable(_cts.Token))
                {

                    // 自动等待新数据
                    Console.WriteLine($"Consumed from BlockingCollection: {item}, 当前个数:{_blockingCollection.Count}");
                }
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Consumption canceled");
            }
        });
    }

    // 停止所有操作
    public void Stop()
    {
        _cts.Cancel();
    }
}

//使用示例
var demo = new QueueDemo();
demo.ProduceData();
demo.ConsumeData();

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

demo.Stop();

方案 2:使用 Channel(异步模型 - 推荐)

csharp 复制代码
public class ChannelDemo
{
    private readonly Channel<int> _channel = Channel.CreateUnbounded<int>(
        new UnboundedChannelOptions { SingleWriter = false, SingleReader = false }
    );

    private readonly CancellationTokenSource _cts = new CancellationTokenSource();

    // 生产者(异步写入)
    public async Task ProduceAsync()
    {
        while (true)
        {
            var item = GenerateItem();
            await _channel.Writer.WriteAsync(item); // 非阻塞写入
            Console.WriteLine($"Produce: {item}");
            await Task.Delay(20);
        }
    }

    // 消费者(异步读取)
    public async Task ConsumeAsync()
    {
        while (await _channel.Reader.WaitToReadAsync())
        {
            if (_channel.Reader.TryRead(out var item))
            {
                await ProcessItemAsync(item);
            }
        }
    }

    private int GenerateItem() => new Random().Next(1, 100);
    private async Task ProcessItemAsync(int item)
    {
        await Task.Delay(100); // 模拟异步处理
        Console.WriteLine($"Processed: {item}");
    }

    // 停止所有操作
    public void Stop()
    {
        _cts.Cancel();
    }
}

三、选型建议

相关推荐
李高钢10 小时前
C# WinForms 架构与项目实战:多窗体通信、三层架构、数据库、日志与配置
架构·c#·winforms
OPEN-F12 小时前
Python进阶教程:自动化办公实战
python·c#·自动化
码农刚子13 小时前
用 .NET 8 + uni-app 做一套多租户畜牧 SaaS:我们在秦巴牧云踩过的 6 个坑
uni-app·.net·saas
czhc114007566313 小时前
C# 处理体数据:装、钉、交、取消
c#
淡海水14 小时前
03-03-线性-LinkedList-T-源码不变式与选择边界
windows·链表·c#·编译·linkedlist·clr·机器码
longxiaozhang61 天前
C# Array类:数组其实没那么难
开发语言·c#
longxiaozhang61 天前
C#运算符重载:让对象也能使用“加减乘除”
开发语言·c#
花落人散处1 天前
C# 核心编程知识点总结:网络编程、委托与事件、异步编程与LINQ
c#
fl1768312 天前
基于C#WPF实现的内存加速球清理类似360加速球内存清理
开发语言·c#·wpf
CoderIsArt2 天前
OPC UA 完整介绍、官网、C# 简单使用
开发语言·c#