0

0

c# 如何在c#中实现一个异步的生产者消费者队列

畫卷琴夢

畫卷琴夢

发布时间:2026-01-17 11:41:02

|

239人浏览过

|

来源于php中文网

原创

推荐用 Channel 实现线程安全异步生产者消费者队列,它无锁、轻量、原生支持 async/await;避免误用 ConcurrentQueue 或 BlockingCollection 的同步阻塞操作。

c# 如何在c#中实现一个异步的生产者消费者队列

Channel 实现线程安全的异步生产者消费者队列

直接上结论:C# 6+(.NET Core 2.1+)推荐用 System.Threading.Channels.Channel,它专为高并发异步场景设计,比手写 BlockingCollection + Task 组合更轻量、无锁、原生支持 async/await

常见错误是试图用 ConcurrentQueue 自己封装 awaitable 操作——它本身不提供异步等待能力,强行加 Task.Delay 或轮询会浪费 CPU;也有人误用 BlockingCollectionTake(),它会同步阻塞线程,破坏 async 上下文。

  • Channel.CreateBounded() 创建有界通道,溢出时可配置拒绝策略(如 DropWrite 或抛异常)
  • Channel.CreateUnbounded() 适合写入吞吐优先、内存可控的场景
  • 生产端调用 Writer.WriteAsync(item),消费端用 Reader.ReadAsync() —— 两者都返回 ValueTask,无分配开销
  • 通道关闭后,Reader.ReadAsync() 会完成并返回 default(T),需配合 TryRead 或检查 WaitToReadAsync().IsCompletedSuccessfully
var channel = Channel.CreateBounded(100);

// 生产者 _ = Task.Run(async () => { for (int i = 0; i < 5; i++) { await channel.Writer.WriteAsync($"msg-{i}"); await Task.Delay(100); } channel.Writer.Complete(); });

// 消费者 _ = Task.Run(async () => { await foreach (var msg in channel.Reader.ReadAllAsync()) { Console.WriteLine($"Received: {msg}"); } });

如何正确处理消费者异常与通道终止

消费者任务崩溃或未捕获异常会导致 ReadAllAsync() 提前退出,但通道可能仍有未读项;更隐蔽的问题是:生产者调用 Complete() 后,若消费者没读完就退出,剩余数据会丢失。

  • 必须在 await foreach 外层包 try/catch,否则异常会静默终止迭代
  • 若需确保所有已入队消息被处理,不要依赖 channel.Writer.Complete() 触发消费者退出——应另设信号(如 CancellationToken)协调停机
  • Reader.CompletionTask,反映消费者侧是否完成(包括异常终止),可用于 await channel.Reader.Completion 等待消费结束
  • 避免在消费者中直接 await channel.Reader.Completion —— 它不会等未读项,只等迭代器退出

什么时候不该用 Channel

不是所有“队列”需求都适合 Channel。它定位是“流式数据传输”,不提供随机访问、计数查询、中间件插拔等能力。

Evoker
Evoker

一站式AI创作平台

下载
  • 需要实时获取当前队列长度?Channel.Reader.Count 只在有界通道且未被并发写入时可靠;无界通道返回 -1
  • 要支持多个消费者竞争消费同一消息?Channel 是单消费者语义;此时该用 ServiceBusRabbitMQ
  • 消息需持久化、重试、死信?Channel 纯内存,崩溃即丢;必须外接存储层
  • 项目还在 .NET Framework 4.8?Channel 不可用,只能降级用 BlockingCollection + GetConsumingEnumerable()(但无法真正异步)

替代方案:BlockingCollection 的异步包装陷阱

有人用 Task.Run(() => collection.Take()) 伪异步,这本质是线程池抢占,增加调度开销且无法取消;正确做法是仅在必须兼容旧框架时,用 TryTake 配合短时 Task.Delay 循环,但务必设超时和取消令牌。

  • 永远不要写 await Task.Run(() => collection.Take()) —— 这违背 async/await 减少线程占用的初衷
  • 若坚持用 BlockingCollection,消费循环应类似:while (collection.TryTake(out var item, 10, token)) { ... }
  • BlockingCollectionAdd 在有界模式下可能阻塞线程,而 Channel.Writer.WriteAsync 在满时默认返回 ValueTask 并挂起,更可控

实际落地时,最易忽略的是消费者异常传播路径和通道生命周期管理——写个 await foreach 很容易,但谁负责捕获异常?谁决定何时 Complete()?这些边界不厘清,上线后就会出现消息静默丢失或消费者假死。

相关专题

更多
rabbitmq和kafka有什么区别
rabbitmq和kafka有什么区别

rabbitmq和kafka的区别:1、语言与平台;2、消息传递模型;3、可靠性;4、性能与吞吐量;5、集群与负载均衡;6、消费模型;7、用途与场景;8、社区与生态系统;9、监控与管理;10、其他特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

201

2024.02.23

什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

178

2024.05.11

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

212

2025.12.18

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

197

2023.11.20

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

88

2023.09.25

php中foreach用法
php中foreach用法

本专题整合了php中foreach用法的相关介绍,阅读专题下面的文章了解更多详细教程。

42

2025.12.04

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6091

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

805

2023.09.14

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

27

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号