我最近一直在熟悉.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示例是这样的:

  1. static async Task Main(string[] args)
  2. {
  3. var myChannel = Channel.CreateUnbounded();
  4.  
  5. for (int i = 0; i < 10; i++)
  6. {
  7. await myChannel.Writer.WriteAsync(i);
  8. }
  9.  
  10. while (true)
  11. {
  12. var item = await myChannel.Reader.ReadAsync();
  13. Console.WriteLine(item);
  14. }
  15. }

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

Channel是线程安全的

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

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

  1. static async Task Main(string[] args)
  2. {
  3. var myChannel = Channel.CreateUnbounded();
  4.  
  5. _ = Task.Factory.StartNew(async () =>
  6. {
  7. for (int i = 0; i < 10; i++)
  8. {
  9. await myChannel.Writer.WriteAsync(i);
  10. await Task.Delay(1000);
  11. }
  12. });
  13.  
  14. while (true)
  15. {
  16. var item = await myChannel.Reader.ReadAsync();
  17. Console.WriteLine(item);
  18. }
  19. }

在这里,我们有一个单独的线程写入消息,而我们的主线程读取消息。你会注意到有趣的事情是,我们添加了消息之间的延迟。怎么能调用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. Robot Framework+adb框架自动化测试Android设备案例⑸——L1层测试用例

    一.L1层测试用例 1.初始化.robot *** Settings *** Resource ../L2层关键字.robot *** Test Cases *** 切换EMMC模式 [Tags] A ...

  2. Java并发编程的艺术(三)——synchronized

    什么是synchronized synchronized可以保证某个代码块或者方法被一个线程占有,保证了一个线程的可先性.java 1.6之前是重量级锁,在1.6进行了各种优化,就不那么重了,并引入了 ...

  3. cocosCreator微信小游戏排行榜思路

    cocosCreator制作微信小游戏排行榜实现方案: 游戏认知:项目分为主域和子域,主域就是游戏主程部分,子域为单独处理微信排行榜公共域数据的. 游戏主域里创建一个节点,添加WXSubContext ...

  4. Day7 python高级特性-- 切片 Slice

    先举一个例子,取list或tuple中的某几个元素:     1.取 ['a','b','c','d','e','f'] 第1.2.5.6个元素:        >>> a = [' ...

  5. centos7 安装netstat命令工具

    [root@node01 yum.repos.d]# yum install -y net-tools Loaded plugins: fastestmirror Loading mirror spe ...

  6. Docker 安装并部署Tomcat、Mysql8、Redis

    1.  安装前检查 1 #ContOS 7安装Docker系统为64位,内核版本为3.10+ 2 lsb_release -a 3 4 uname -r 5 6 #更新yum源 7 yum -y up ...

  7. Nacos源码深度解析1-服务注册初始化(客户端)

    一.初始化 NamingService naming = NamingFactory.createNamingService(properties); 二.通过反射传入properties生成Naco ...

  8. js下 Day09、事件(二)

    一.事件流 事件流描述的是从页面中接收事件的顺序,目前主要有三个模型: #1. 事件冒泡: 事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素

  9. js下 Day01、DOM对象,BOM浏览器对象模型

    一.初识DOM 1.什么是DOM?为什么学习DOM 2.DOM是实现js在网页实现交互的关键环节,我们的js代码就是通过DOM的方法来实现对于html内容的操作. 3.认识DOM实现了js和网页结合的 ...

  10. Hadoop核心-HDFS

    上一篇我们熟悉了hadoop,本篇讲解一下hadoop第一个核心HDFS. 一.概述 HDFS是一个分布式文件存储系统,以流式数据访问模式存储超大文件,将数据分块存储到一个商业硬件集群内的不同机器上, ...