新闻中心

.NET如何实现一个生产者-消费者队列

2025-11-01
浏览次数:
返回列表
最推荐使用System.Threading.Channels实现生产者-消费者队列。它支持有界和无界通道,提供异步操作与背压机制,适用于多种应用场景,尤其适合现代异步编程模型。

.net如何实现一个生产者-消费者队列

在 .NET 中实现生产者-消费者队列,最推荐的方式是使用 System.Threading.Channels 命名空间中的 Channel 类。它是微软官方为现代异步场景设计的高性能、线程安全的生产者-消费者队列组件,适用于控制台应用、ASP.NET、后台服务等多种场景。

1. 使用 System.Threading.Channels 实现

Channel 提供了两种模式:有界(Bounded)和无界(Unbounded)。它支持同步和异步操作,天然适配 async/await。

安装包(.NET 6 以下可能需要):

Install-Package System.Threading.Channels

示例:异步生产者-消费者模型

创建一个有界通道,启动一个生产者任务和一个消费者任务:

using System;
using System.Threading.Channels;
using System.Threading.Tasks;

var channel = Channel.CreateBounded<string>(10); // 最多存放10个消息

// 生产者
_ = Task.Run(async () =>
{
    for (int i = 1; i <= 5; i++)
    {
        await channel.Writer.WriteAsync($"消息 {i}");
        Console.WriteLine($"生产: 消息 {i}");
        await Task.Delay(100); // 模拟耗时
    }
    channel.Writer.Complete(); // 关闭写入端
});

// 消费者
_ = Task.Run(async () =>
{
    await foreach (var msg in channel.Reader.ReadAllAsync())
    {
        Console.WriteLine($"消费: {msg}");
        await Task.Delay(150); // 模拟处理时间
    }
    Console.WriteLine("消费完成。");
});

// 等待一段时间让任务执行
await Task.Delay(2000);

说明:

  • WriteAsync 是线程安全的,当通道满时会自动等待。
  • ReadAllAsync 返回 IAsyncEnumerable,自动监听新消息。
  • 调用 Complete() 表示不再有新数据,消费者会在所有消息处理完后退出。

2. 多生产者或多消费者场景

默认情况下,Channel 支持多个生产者,但只有一个消费者能安全读取。若需多消费者,应使用 UnboundedChannelOptions 或确保并发控制。

允许多个消费者竞争消费:

NameGPT NameGPT

免费的名称生成器,AI驱动在线生成企业名称及Logo

NameGPT 119 查看详情 NameGPT ```csharp var options = new BoundedChannelOptions(10) { FullMode = BoundedChannelFullMode.Wait, SingleReader = false, // 允许多个消费者 SingleWriter = false // 允许多个生产者 };

var channel = Channel.CreateBounded(options);

// 启动3个消费者 for (int i = 0; i { await foreach (var item in channel.Reader.ReadAllAsync()) { Console.WriteLine($"消费者 {consumerId} 处理: {item}"); await Task.Delay(50); } }); }

<H3>3. 替代方案:BlockingCollection(传统方式)</H3>
<p>在较老项目或不支持 Channel 的框架中,可以使用 <code>BlockingCollection<T></code> 配合 <code>ConcurrentQueue<T></code>。</p>

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

var collection = new BlockingCollection<string>(new ConcurrentQueue<string>());

// 生产者
_ = Task.Run(() =>
{
    for (int i = 1; i <= 5; i++)
    {
        collection.Add($"消息 {i}");
        Console.WriteLine($"生产: {i}");
        Task.Delay(100).Wait();
    }
    collection.CompleteAdding(); // 停止添加
});

// 消费者
_ = Task.Run(() =>
{
    foreach (var msg in collection.GetConsumingEnumerable())
    {
        Console.WriteLine($"消费: {msg}");
        Task.Delay(150).Wait();
    }
});

注意:BlockingCollection 是同步阻塞的,不如 Channel 适合异步流处理。

4. 在 ASP.NET 中的应用建议

不要在 Web 请求中直接启动后台任务。建议结合 IHostedService 或使用 BackgroundService 来运行消费者。

public class MessageConsumerService : BackgroundService
{
    private readonly ChannelReader<string> _reader;

    public MessageConsumerService(ChannelReader<string> reader)
    {
        _reader = reader;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await foreach (var msg in _reader.ReadAllAsync(stoppingToken))
        {
            Console.WriteLine($"后台处理: {msg}");
        }
    }
}

注册服务:

```csharp var channel = Channel.CreateUnbounded(); services.AddSingleton(channel.Reader); services.AddSingleton(channel.Writer); services.AddHostedService(); ```

基本上就这些。优先使用 System.Threading.Channels,它简洁、高效、支持背压(backpressure),是现代 .NET 推荐的实现方式。

以上就是.NET如何实现一个生产者-消费者队列的详细内容,更多请关注其它相关文章!


# 两种  # 品牌网站建设框架  # 网客营销推广文案  # 西区网站seo优化排名  # 知名的seo排名托管  # seo自学得需要多少天  # 黑seo是什么意思  # 罗湖门户网站优化怎么做  # 网站优化推广律师频道  # 昆明seo方法  # 网站产品推广视频  # 推荐使用  # 队列  # 最多  # 无界  # 默认值  # 适用于  # 如何实现  # 怎么处理  # 多个  # .net  # 微软  # ai  # 生产者-消费者 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: C++ string find函数返回值npos详解_C++字符串查找失败的判断条件  qq游戏免费畅玩入口_qq游戏电脑版快速启动  不同用户不同价格! 索尼开启账户个性化定价测试  Golang如何使用bytes.Split分割字节切片_Golang bytes切片分割方法  KFC游戏互动怎么赢取优惠券_KFC线上游戏活动参与与优惠代码赢取教程  Go语言中对Map值调用带指针接收者方法:原理与最佳实践  如何在CSS中使用visited与link控制链接颜色_visited link伪类配合  如何创建独立于主系统的J*a运行环境_隔离式环境搭建策略  如何优雅地扩展SprykerGlue后端API授权逻辑,使用spryker/glue-backend-api-application-authorization-connector-extension  神庙逃亡小游戏在线玩 神庙逃亡小游戏入口  文本文档写html代码怎么运行_文本文档html代码运行步骤【教程】  QQ邮箱官方网页版登录 QQ邮箱个人邮箱快速访问  LINUX下如何进行磁盘分区_fdisk与parted工具在LINUX中的使用对比  在Go语言中利用后缀数组处理多字符串:实现高效文本匹配与自动补全  C++的std::forward_list怎么用_C++ STL中单向链表容器的特点与应用  在J*a中如何开发简易博客标签推荐系统_博客标签推荐项目实战解析  c++中的const_cast和reinterpret_cast怎么用_c++四种类型转换  谷歌浏览器无痕模式怎么开 Chrome开启无痕浏览设置方法【教程】  mysql如何设置表访问权限_mysql表访问权限配置  HTML转PPT成品工具有哪些?HTML网页转PPT成品工具大全  中兴Axon42Ultra怎样在文件App筛图_iPhone中兴Axon42Ultra文件App筛图【图片筛选】  深入理解J*a编译器的兼容性选项:从-source到--release  Win11怎么关闭触摸屏_Windows 11禁用HID符合标准触摸屏  小红书网页版入口链接分享 小红书官网直接进  QQ邮箱正确登录入口_QQ邮箱官方网站使用地址  html怎么运行外部js文件中的函数_运html外js文件函数法【技巧】  Typer应用中动态命令行参数的解析与处理  响应式容器内容自动缩放与宽高比维持教程  wps文字怎么插入目录并自动更新_wps文字如何插入目录并自动更新方法  vivo浏览器自带的下载器速度慢怎么办 vivo浏览器提升文件下载速度的技巧  怎样更改Windows系统的默认安装路径_避免C盘爆满的终极设置【技巧】  在Runstone环境中高效处理TasteDive API的JSON数据  在J*a中如何捕获IndexOutOfBoundsException_索引越界异常防护方法说明  J*aScript中在Map循环中检测并处理空数组元素  css子元素高度不一致导致布局错位怎么办_使用align-items:stretch解决高度差异  TikTok国际版网页端快速入口 TikTok全球版短视频浏览教程  Golang如何测试channel通信行为_Golang channel通信测试与分析方法  《北京人工智能产业白皮书(2025)》发布:全年核心产值预计突破 4500 亿元  如何使用spryker/configurable-bundles-products-resource-relationship模块解决复杂产品捆绑关系难题  处理Kafka消费者会话超时:深入理解消息处理语义与幂等性  小米汽车11月交付量突破40000台!雷军:将继续努力  在J*a中如何使用BigDecimal进行高精度计算_BigDecimal类应用指南  Golang如何处理RPC请求负载均衡_Golang RPC请求负载均衡策略与实践  如何使用Go和Martini动态服务解码后的图片  腾讯QQ邮箱官方网站_QQ邮箱网页版在线登录  漫蛙2漫画入口 漫蛙正版网页漫画直达网址  漫画星球免费下拉式入口 漫画星球免费漫画在线阅读网站  vivo云服务网页版登录 怎么登录vivo云服务网页版  Win10桌面图标出现小盾牌怎么办 Win10去除UAC图标教程【解决】  J*aScript设计模式实践_j*ascript代码优化 

搜索