一文读懂 .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(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...
随机推荐
- 入坑java工程师那些事
最近在知乎上看到好多关于转行做java的朋友,有的在担心学历,有的在想着如何学习java,有的在纠结如何面试.作为一个工作了近10年的java程序员来聊聊我的一些想法,主要从学历.个人能力.工作环境等 ...
- 阿里云服务器安装Docker并部署nginx、jdk、redis、mysql
阿里云服务器安装Docker并部署nginx.jdk.redis.mysql 一.安装Docker 1.安装Docker的依赖库 yum install -y yum-utils device-map ...
- python 定时任务APScheduler 使用介绍
python 定时任务APScheduler 使用介绍 介绍: APScheduler的全称是Advanced Python Scheduler.它是一个轻量级的 Python 定时任务调度框架. ...
- GO语言的JSON02---反序列化
package main import ( "encoding/json" "fmt" ) type Human struct { Name string Ag ...
- QT绘制简易表盘
1.简介 最近学习了一下QT,熟悉了一段时间后发现它的功能还是挺强大的,同时也比较方便用户上手去使用.现在就基于最近学习的内容,实现一个简易的带指针旋转功能的表盘.文中表盘的实现是基于QT的QPain ...
- 深度学习加速器堆栈Deep Learning Accelerator Stack
深度学习加速器堆栈Deep Learning Accelerator Stack 通用张量加速器(VTA)是一种开放的.通用的.可定制的深度学习加速器,具有完整的基于TVM的编译器堆栈.设计了VTA来 ...
- 孟老板 BaseAdapter封装 (二) Healer,footer
BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...
- swagger 注解使用
@Api() 用于类:表示标识这个类是swagger的资源 tags–表示说明 value–也是说明,可以使用tags替代 但是tags如果有多个值,会生成多个list @ApiOperation() ...
- ABAP SORT排序注意点
SORT TABLE BY XXX XXX .如果不加任何语法都是默认升序,延伸可以用ASCENDING和DESCENDING对具体的字段进行升序和降序排列. 简单通俗的来阐释一下,在字段名后面加AS ...
- 【NX二次开发】Block UI 枚举
属性: 常规 类型 描述 BlockID String 控件ID Enable Logical 是否可操作 Group Logical ...