介绍

System.Threading.Channels 是.NET Core 3.0 后推出的新的集合类型, 具有异步API,高性能,线程安全等特点,它可以用来做消息队列,进行数据的生产和消费, 公开的 WriterReader 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 的 SingleWriterSingleReader,来指定 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

源码

https://github.com/dotnet/runtime/tree/main/src/libraries/System.Threading.Channels/src/System/Threading/Channels

CAP

https://github.com/dotnetcore/CAP

Quic

https://github.com/dotnet/runtime/tree/main/src/libraries/System.Net.Quic

一文读懂 .NET 中的高性能队列 Channel的更多相关文章

  1. 一文读懂Java中的动态代理

    从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...

  2. 一文读懂BERT中的WordPiece

    1. 前言 2018年最火的论文要属google的BERT,不过今天我们不介绍BERT的模型,而是要介绍BERT中的一个小模块WordPiece. 2. WordPiece原理 现在基本性能好一些的N ...

  3. 一文读懂JS中的原型和原型链(图解)

    讲原型的时候,我们应该先要记住以下几个要点,这几个要点是理解原型的关键: 1.所有的引用类型(数组.函数.对象)可以自由扩展属性(除null以外). 2.所有的引用类型都有一个’_ _ proto_ ...

  4. 一文读懂高性能网络编程中的I/O模型

    1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的 ...

  5. 一文读懂神经网络训练中的Batch Size,Epoch,Iteration

    一文读懂神经网络训练中的Batch Size,Epoch,Iteration 作为在各种神经网络训练时都无法避免的几个名词,本文将全面解析他们的含义和关系. 1. Batch Size 释义:批大小, ...

  6. 一文读懂数仓中的pg_stat

    摘要:GaussDB(DWS)在SQL执行过程中,会记录表增删改查相关的运行时统计信息,并在事务提交或回滚后记录到共享的内存中.这些信息可以通过 "pg_stat_all_tables视图& ...

  7. 从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

    本文原作者阮一峰,作者博客:ruanyifeng.com. 1.引言 HTTP 协议是最重要的互联网基础协议之一,它从最初的仅为浏览网页的目的进化到现在,已经是短连接通信的事实工业标准,最新版本 HT ...

  8. [转帖]从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

    从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路   http://www.52im.net/thread-1709-1-2.html     本文原作者阮一峰,作者博客:r ...

  9. 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?

    本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,感谢作者的无私分享. 1.引言   Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...

随机推荐

  1. Spring-Cloud之Feign原理剖析

    Feign 主要是帮助我们方便进行rest api服务间的调用,其大体实现思路就我们通过标记注解在一个接口类上(注解上将包含要调用的接口信息),之后在调用时根据注解信息组装好请求信息,接下来基于rib ...

  2. 在react中使用redux并实现计数器案例

    React + Redux 在recat中不使用redux 时遇到的问题 在react中组件通信的数据是单向的,顶层组件可以通过props属性向下层组件传递数据,而下层组件不能向上层组件传递数据,要实 ...

  3. .NET6系列:C#10新功能预览

    系列目录     [已更新最新开发文章,点击查看详细] 2021年4月19日微软发布公告称将于今年夏季发布首款64位的 Visual Studio 2022,2021年5月20日又发布了 Visual ...

  4. MongoDB(13)- 查询操作返回指定的字段

    插入测试数据 db.inventory.insertMany( [ { item: "journal", status: "A", size: { h: 14, ...

  5. TensorFlow基础剖析

    TensorFlow基础剖析 一.概述 TensorFlow 是一个使用数据流图 (Dataflow Graph) 表达数值计算的开源软件库.它使 用节点表示抽象的数学计算,并使用 OP 表达计算的逻 ...

  6. 视频系列:RTX实时射线追踪(上)

    视频系列:RTX实时射线追踪(上) Video Series: Practical Real-Time Ray Tracing With RTX RTX在游戏和应用程序中引入了一个令人兴奋的和根本性的 ...

  7. 使用multus实现管理网和业务网分离——calico和flannel共存

    多个网络层面的需求 一开始为k8s集群搭建了calico网络,所有的容器都用calico对应的网卡进行通信.为了实现网络监控的清爽,想把管理组件,例如日志.统计.监控等组件挪到另外一个网络.于是产生一 ...

  8. Hadoop 数据迁移用法详解

    数据迁移使用场景 冷热集群数据分类存储,详见上述描述. 集群数据整体搬迁.当公司的业务迅速的发展,导致当前的服务器数量资源出现临时紧张的时候,为了更高效的利用资源,会将原A机房数据整体迁移到B机房的, ...

  9. Java 反射编程(上)

    文章目录 反射的泛型就是用`? `来描述 反射与类的操作 (取得父类信息) 取得父类信息 1. 获得本类的包名称: 2. 取得父类的Class 对象 3. 取得父类接口 案例: 使用上述方法 反射与类 ...

  10. springboot的restful风格获取请求中携带的参数

    http://localhost:8080/emp/1 有以上请求,我们controller要怎么获取请求中传递的参数1呢? 通过PathVariable注解,如下: @DeleteMapping(& ...