一文读懂 .NET 中的高性能队列 Channel

介绍
System.Threading.Channels 是.NET Core 3.0 后推出的新的集合类型, 具有异步API,高性能,线程安全等特点,它可以用来做消息队列,进行数据的生产和消费, 公开的 Writer 和 Reader api对应消息的生产者和消费者,也让Channel更加的简洁和易用,与Rabbit MQ 等其他队列不同的是,Channel 是进程内的队列。
开始Channel之旅
创建一个 channel 非常简单,Channel 类公开的API支持创建无限容量和有限容量的 channel
// 创建有限容量的channel
var channel = Channel.CreateBounded<string>(100);
// 创建无限容量的channel
var channel = Channel.CreateUnbounded<string>();
这里需要注意的是,当你使用一个有限容量的 Channel 时,你需要指定容量的大小,还可以指定一个 BoundedChannelFullMode 的枚举类型,来告诉 channel 达到容量限制的时候,继续写入时应该怎么处理
public enum BoundedChannelFullMode
{
Wait,
DropNewest,
DropOldest,
DropWrite
}
- Wait 是默认值,当 channel 容量满了以后,写入数据时会返回 false,直到channel有数据被消费了以后,才可以继续写入
- DropNewest 移除最新的数据,也就是从队列尾部开始移除
- DropOldest 移除最老的数据,也就是从队列头部开始移除
- DropWrite 写入数据返回成功,但是转头就把刚才的数据丢了
// 创建有限容量的channel, 并指定容量达到最大的策略
var channel = Channel.CreateBounded<string>(new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.Wait
});
生产数据
生产数据主要通过 Channel 提供的 Writer api, 常规的写入操作如下:
await channel.Writer.WriteAsync("hello");
Channel 还提供了 TryWrite() 方法,如果写入数据失败时会返回 false,WaitToWriteAsync() 方法会做非阻塞的等待,直到 Channel 允许写入新的数据时返回 true,同样的 Channel 关闭后会返回 false,翻了一下源码发现,WriteAsync() 方法内部其实是调用了 TryWrite() 和 WaitToWriteAsync() 方法。
消费数据
消费数据主要通过 Channel 提供的 Reader api, 常规的读取操作如下:
var item = await channel.Reader.ReadAsync();
同样的,Channel 提供了 TryRead() 尝试读取数据,WaitToReadAsync() 方法会做非阻塞的等待,直到 Channel 可以读取到数据时会返回 true,在 Channel 关闭后会返回 false, ReadAsync() 的方法内部其实是调用了 TryRead() 和 WaitToReadAsync() 方法, 另外你可以通过 channel.Reader.Count 获取队列元素的数量。
在实际的使用场景中,可能需要一些后台任务,长时间的进行消费,那么你可以使用下边的方式
while (await channel.Reader.WaitToReadAsync())
{
while (channel.Reader.TryRead(out var item))
{
Console.WriteLine(item);
}
}
ReadAllAsync() 方法返回的是一个 IAsyncEnumerable<T> 对象,也可以用 await foreach 的方式来获取数据
await foreach(var item in channel.Reader.ReadAllAsync())
{
Console.WriteLine(item);
}
单一生产者和消费者
创建 Channel 时,可以设置 ChannelOptions 的 SingleWriter 和 SingleReader,来指定 Channel 时单一的生产者和消费者,默认都是 false,当设置了 SingleWriter = true 时, 会限制同一个时间只能有一个生产者可以写入数据, SingleReader = true 是同样的。
另外,如果只需要一个消费者的话,你应该设置 SingleReader = true, Channel 在内部做了一些优化,在读取时避免了锁操作,性能上有些许的提升。
性能
这里的基准测试我对比了三种类型,Channel, BufferBlock, BlockingCollection,分别写入了10000条数据,然后进行读取,发现 Channel 确实是表现比较好。

总结
Channel 实际上还是使用 ConcurrentQueue做的封装, 使用起来更方便,对异步更友好,另外,.NET 5 其中的 Quic 内部就使用了Channel,CAP 也在新版本中使用 Channel 替换掉了之前的 BlockingCollection,来实现进程内的队列。
官方介绍
https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels
Quic
https://github.com/dotnet/runtime/tree/main/src/libraries/System.Net.Quic
一文读懂 .NET 中的高性能队列 Channel的更多相关文章
- 一文读懂Java中的动态代理
从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...
- 一文读懂BERT中的WordPiece
1. 前言 2018年最火的论文要属google的BERT,不过今天我们不介绍BERT的模型,而是要介绍BERT中的一个小模块WordPiece. 2. WordPiece原理 现在基本性能好一些的N ...
- 一文读懂JS中的原型和原型链(图解)
讲原型的时候,我们应该先要记住以下几个要点,这几个要点是理解原型的关键: 1.所有的引用类型(数组.函数.对象)可以自由扩展属性(除null以外). 2.所有的引用类型都有一个’_ _ proto_ ...
- 一文读懂高性能网络编程中的I/O模型
1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的 ...
- 一文读懂神经网络训练中的Batch Size,Epoch,Iteration
一文读懂神经网络训练中的Batch Size,Epoch,Iteration 作为在各种神经网络训练时都无法避免的几个名词,本文将全面解析他们的含义和关系. 1. Batch Size 释义:批大小, ...
- 一文读懂数仓中的pg_stat
摘要:GaussDB(DWS)在SQL执行过程中,会记录表增删改查相关的运行时统计信息,并在事务提交或回滚后记录到共享的内存中.这些信息可以通过 "pg_stat_all_tables视图& ...
- 从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路
本文原作者阮一峰,作者博客:ruanyifeng.com. 1.引言 HTTP 协议是最重要的互联网基础协议之一,它从最初的仅为浏览网页的目的进化到现在,已经是短连接通信的事实工业标准,最新版本 HT ...
- [转帖]从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路
从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路 http://www.52im.net/thread-1709-1-2.html 本文原作者阮一峰,作者博客:r ...
- 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?
本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,感谢作者的无私分享. 1.引言 Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...
随机推荐
- 离散傅里叶变换的衍生,负频率、fftshift、实信号、共轭对称
封面是福州的福道,从高处往下看福道上的人在转圈圈.从傅里叶变换后的频域角度来看,我们的生活也是一直在转圈圈,转圈圈也是好事,说明生活有规律,而我们应该思考的是,如何更有效率地转圈圈--哦别误会,我真不 ...
- java学习之旅2——set
var set = Collections.synchronizedSet(new HashSet<Integer>()); 可以这样来获得一个同步的集合. 对于HashSet, for循 ...
- C 语言通用模板队列
前言 嵌入式开发过程中,各个模块之间,各个设备之间进行交互时,都会存在数据的输入输出,由于处理的方式不同,数据不会立即同步处理,因此通常在设计时都会设计缓冲区进行数据的处理,方式数据丢失等问题:一个项 ...
- Prometheus监控软件部署方法
背景:负责基于区块链的某公正项目的状态上报模块设计编码,基于Prometheus进行二次开发 1.说明Prometheus 是一个开源的服务监控软件,它通过 HTTP 协议从远程机器收集数据并存储在本 ...
- Lua 业务 脚本
Lua 脚本 #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice ...
- PVD与CVD性能比较
PVD与CVD性能比较 CVD定义: 通过气态物质的化学反应在衬底上淀积一层薄膜材料的过程. CVD技术特点: 具有淀积温度低.薄膜成分和厚度易于控制.均匀性和重复性好.台阶覆盖优良.适用范围广.设备 ...
- YOLOv4没交棒,但YOLOv5来了!
YOLOv4没交棒,但YOLOv5来了! 前言 4月24日,YOLOv4来了! 5月30日,"YOLOv5"来了! 这里的 "YOLOv5" 是带有引号的,因为 ...
- pytest基础简介及实践举例
一.pytest简介 pytest 是 python 的第三方单元测试框架,比自带的 unittest 更简洁和高效,同时兼容 unittest 框架.它还有如下优点: 1.简单灵活,容易上手,文档丰 ...
- jquery给动态生成的元素绑定事件,on函数
首先先解释一下什么是动态生成的元素:动态生成的元素即我们用jquery的内部插入函数append()所生成的html代码.相对的也有静态生成的元素:即直接编写在页面的html代码. 下面通过例子来讲解 ...
- 【NX二次开发】获取对象边界包容盒的三个函数UF_MODL_ask_bounding_box
今天看到胡工对bounding_box的分享,我也来测试一番(原帖地址:https://www.ugapi.com/thread-10287.html) 获取对象的边界盒子的三个函数: 1 UF_MO ...