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

三、选型建议

相关推荐
hhw19911236 分钟前
c#面试题整理6
java·开发语言·c#
黄金小码农1 小时前
c# 2025/3/8 周六
c#
hhw1991122 小时前
c#面试题整理8
c#
飞向星河4 小时前
SV学习笔记——数组、队列
笔记·学习·c#
緣木求魚7 小时前
c#结合IL(中间语言)分析Try-Catch的内部机制及其对性能的影响
c#·il
qq_297908017 小时前
c#财务软件专业版企业会计做账软件财务管理系统软件
sqlserver·开源·c#
咩咩觉主7 小时前
C# &Unity 唐老狮 No.7 模拟面试题
开发语言·unity·c#
咩咩觉主7 小时前
Unity网络开发基础 (2) 网络协议基础
网络·unity·c#
Nicole Potter7 小时前
内存泄漏出现的时机和原因,如何避免?
c++·游戏·面试·c#