我最近一直在熟悉.net Core中引入的新Channel<T>类型。我想在它第一次发布的时候我了解过它,但是有关文章非常非常少,我不能理解它们与其他队列有什么不同。

在使用了一段时间后,我终于看到了它们的吸引力和真正的力量。最值得注意的是大型异步后台操作,这些操作几乎需要双向通信来同步它们正在做的事情。这句话有点拗口,但希望在本系列文章结束时,你会清楚什么时候应该使用Channel<T>,什么时候应该使用一些更基本的东西,比如Queue<T>。

Channel是什么?

从核心来说,Channel本质上是.net中的一种新的集合类型,它与现有的Queue<T>类型非常相似,但有额外的好处。在真正尝试研究这个主题时,我发现的问题是,许多现有的外部队列技术(IBM MQ、Rabbit MQ等)都有“channel”的概念,它们的范围从完全抽象的思维过程,到系统中实际的物理类型。

现在也许我大错特错,但如果你认为.net中的Channel就好比是允许等待新消息的一个队列,并告诉生产者要保持队列越来越大,消费者无法跟上,我认为这很难出错。

这里我提到了一个关键词,生产者/消费者。你可能还听说过Pub/Sub。但它们是不可互换的。

Pub/Sub描述的是某人发布信息,一个或多个“订阅者”监听该信息并对其采取一定的响应行为。这里不存在负载平衡,因为当添加订阅服务器时,它们本质上与其他所有人获得相同消息的副本。

在图表形式中,Pub/Sub看起来有点像这样:

生产者/消费者描述生产者发布消息的行为,并且有一个或多个消费者可以对该消息进行操作,但是每个消息只读取一次。它不会分发到每个订阅者。

当然,用图表的形式:

另一种思考生产者/消费者的方式是想象你去超市结账。当顾客想结帐时,排队的队伍变长了,你可以简单地打开更多的收银台来处理这些顾客。这个小小的思考过程实际上是很重要的,因为如果你不能打开更多的收银台怎么办?排队的队伍应该越来越长吗?如果收银台操作员坐在那里,但没有顾客怎么办?他们是应该当天就打包回家呢,还是应该被告知坐着等客人来了再说。

这通常被称为生产者-消费者问题,这是Channel要解决的问题。

基础Channel示例

与Channel有关的所有东西都在System.Threading.Channels中。在以后的版本中,这似乎是与标准的.net Core项目捆绑在一起的,但如果不是,这里有一个nuget包:https://www.nuget.org/packages/System.Threading.Channels。

一个极其简单的Channel示例是这样的:

static async Task Main(string[] args)
{
var myChannel = Channel.CreateUnbounded(); for (int i = 0; i < 10; i++)
{
await myChannel.Writer.WriteAsync(i);
} while (true)
{
var item = await myChannel.Reader.ReadAsync();
Console.WriteLine(item);
}
}

这里没有太多可谈的。我们创建了一个“无限的”通道(这意味着它可以容纳无限项,但在本系列的后续内容中会有更多内容)。我们写10项,读10项,在这一点上,它与我们在.net中见过的任何其他队列没有太大区别。

Channel是线程安全的

没错,通道是线程安全的。这意味着多个线程可以读写同一个通道而不会出现问题。如果我们看一下这里的Channel源代码,我们可以看到它是线程安全的,因为它使用锁和内部“队列”的组合来同步读/写器,一个接一个地读/写。

实际上,Channel的预期用例是多线程场景。例如,如果我们使用上面的基本代码,当我们实际上不需要线程安全性时,维护线程安全性实际上会有一些开销。所以在那个例子中,我们可能只使用Queue<T>更好。但是这段代码呢?

static async Task Main(string[] args)
{
var myChannel = Channel.CreateUnbounded(); _ = Task.Factory.StartNew(async () =>
{
for (int i = 0; i < 10; i++)
{
await myChannel.Writer.WriteAsync(i);
await Task.Delay(1000);
}
}); while (true)
{
var item = await myChannel.Reader.ReadAsync();
Console.WriteLine(item);
}
}

在这里,我们有一个单独的线程写入消息,而我们的主线程读取消息。你会注意到有趣的事情是,我们添加了消息之间的延迟。怎么能调用ReadAsync()?没有TryDequeue或Dequeue,如果队列中没有消息,它就运行null,对吗?

答案是Channel Reader的“ReadAsync()”方法实际上会“等待”一个消息。因此,不需要在等待消息时执行一些荒谬的循环,也不需要在等待时完全阻塞线程。我们将在以后的文章中进一步讨论这个问题,但是你要知道你可以使用ReadAsync来等待新的消息,而不是编写一些自定义的代码来做同样的事情。

接下来是什么?

现在你已经掌握了基础知识,下一篇让我们看看使用Channel一些更高级的场景。

欢迎关注我的公众号,如果你有喜欢的外文技术文章,可以通过公众号留言推荐给我。

原文链接:https://dotnetcoretutorials.com/2020/11/24/using-channels-in-net-core-part-1-getting-started/

在.NET Core中使用Channel(一)的更多相关文章

  1. 在.NET Core中使用Channel(二)

    在我们之前的文章中,看了一些非常简单的例子来说明Channel是如何工作的,我们看到了一些非常漂亮的特性,但大多数情况下它与其他某某Queue实现非常相似.让我们进入一些更高级的话题.我说的是高级,但 ...

  2. 在.NET Core中使用Channel(三)

    到目前为止,我们一直在使用所谓的"Unbounded"通道.你会注意到,当我们创建通道时,我们这样做: var myChannel = Channel.CreateUnbounde ...

  3. ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

    ExpandoObject与DynamicObject的使用   using ImpromptuInterface; using System; using System.Dynamic; names ...

  4. .NET Core中使用RabbitMQ正确方式

    .NET Core中使用RabbitMQ正确方式 首先甩官网:http://www.rabbitmq.com/ 然后是.NET Client链接:http://www.rabbitmq.com/dot ...

  5. .Net Core中使用Grpc

    一.Grpc概述 gRPC 基于如下思想:定义一个服务, 指定其可以被远程调用的方法及其参数和返回类型.gRPC 默认使用protocol buffers作为接口定义语言,来描述服务接口和有效载荷消息 ...

  6. gRPC在 ASP.NET Core 中应用学习(二)

    前言: 上一篇文章中简单的对gRPC进行了简单了解,并实现了gRPC在ASP.NET Core中服务实现.客户端调用:那么本篇继续对gRPC的4中服务方法定义.其他使用注意点进一步了解学习 一.gRP ...

  7. .net core中Grpc使用报错:The remote certificate is invalid according to the validation procedure.

    因为Grpc采用HTTP/2作为通信协议,默认采用LTS/SSL加密方式传输,比如使用.net core启动一个服务端(被调用方)时: public static IHostBuilder Creat ...

  8. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  9. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

随机推荐

  1. PyQt学习随笔:QtDesigner ListView控件列表项的初始化

    在QtDesigner中设计的界面中添加ListView控件后,是没办法添加需要在ListView控件中显示的列表项.由于ListView控件只是一个展示列表项的视图控件,实现了界面与数据的分离,其要 ...

  2. Linux 服务分类

    一,服务分类 1,服务简介与分类 1,服务的分类 启动与自启动 1,服务启动:就是在当前系统中让服务运行,并提供功能 2,服务自启动:自启动是指让服务在系统开机或重启动之后,随着系统的启动而自动启动的 ...

  3. [亲测可用]BCompare文件比较对比工具4.3.4绿色版

    程序员必备良品 Beyond Compare 灰常好用,一直使用的文本 文件比较工具,用来对比两个文件文本的差异,可以用于各种代码,文本对比 使用方法: 解开压缩后:添加右键的 !添加右键.bat,运 ...

  4. 动态svg图片简单制作

    一.简介 #topics #no-box-shadow-img { box-shadow: none } 博主头像 svg图片格式不同于其它图片格式,svg图片本质上是一个xml文件,它内部是标记语言 ...

  5. 抖音数据采集SDK

    接口参数.返回示例请参考文档:抖音api Github仓库:DouyinSDK 0. 初始化 from douyin import AwemeSDK token = 'xxxxxxxx' host = ...

  6. 在Qt中配置海康工业相机SDK及遇到的问题(报错)

    1.在项目的.pro文件里导入海康工业相机的SDK路径 INCLUDEPATH += \ D:\HKVersion\MVS_3.1.0\MVS\Development\Includes #这时到入Op ...

  7. KafkaMirrorMaker 的不足以及一些改进

    背景 某系统使用 Kafka 存储实时的行情数据,为了保证数据的实时性,需要在多地机房维护多个 Kafka 集群,并将行情数据同步到这些集群上. 一个常用的方案就是官方提供的 KafkaMirrorM ...

  8. oracle 11g调优常用语句

    1.查询表的基数及选择性 select a.column_name,       b.num_rows,       a.num_distinct cardinality,       round( ...

  9. Linux shell 获取目录下时间最新的文件的文件名

    ls -lt /dirname/ | grep filename | head -n 1 |awk '{print $9}' 逐条解释: ls -lt /dirname/ 列出此目录下的所有文件并按照 ...

  10. CAP、BASE、ACID

    CAP定理 定义 CAP定理(CAP theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点: 一致性(Consistency) (等同于所有节点访问同一份最新的数据副本:写操作之 ...