.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();
    }
}

三、选型建议

相关推荐
百锦再7 分钟前
.Net配置文件appsetting.json的几种读取方法
chrome·json·.net·依赖注入·appsetting·web.config
昏睡红猹35 分钟前
C#脚本化(Roslyn):如何在运行时引入nuget包
c#
张人玉1 小时前
C# 常量与变量
java·算法·c#
就是有点傻2 小时前
在C#中,可以不实例化一个类而直接调用其静态字段
c#
软件黑马王子2 小时前
C#系统学习第八章——字符串
开发语言·学习·c#
阿蒙Amon2 小时前
C#读写文件:多种方式详解
开发语言·数据库·c#
就是有点傻3 小时前
C#如何实现中英文快速切换
数据库·c#
ChaITSimpleLove4 小时前
.NET9 实现排序算法(MergeSortTest 和 QuickSortTest)性能测试
算法·排序算法·.net·benchmarkdotnet·datadog.trace
一名用户7 小时前
unity实现梦日记式传送组件
后端·c#·unity3d
阿蒙Amon8 小时前
C#扩展方法全解析:给现有类型插上翅膀的魔法
开发语言·c#