我最近一直在熟悉.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. 【WC2014】紫荆花之恋(替罪羊重构点分树 & 平衡树)

    Description 若带点权.边权的树上一对 \((u, v)\) 为 friend,那么需要满足 \(\text{dist}(u, v) \le r_u + r_v\),其中 \(r_x\) 为 ...

  2. python最大几个数和最小几个数(堆排序)

    最大几个数和最小几个数 import heapq a = [7, 5, 3, 4, 8, 6, 0] cc = heapq.nsmallest(2, a) #最小的两个数 dd = heapq.nla ...

  3. Mysql LIMIT的用法

    使用范围 MySQL语句中的limit字句可以帮助我们在使用执行查询的时候,返回数据库中间的数据或者是只提取前几段数据 使用语法 SELECT * FROM table LIMIT [offset,] ...

  4. sql 执行语句函数

    # sql 语句执行函数 def get_access(request): uid=request cursor=connection.cursor() cursor.execute("se ...

  5. Java IO流 FileOutputStream、FileInputStream的用法

    FileOutputStream.FileInputStream的使用 FileOutputStream是OutputStream的继承类,它的主要功能就是向磁盘上写文件.FileOutputStre ...

  6. 多任务-python实现-生成器相关(2.1.13)

    @ 目录 1.概念 2.创建方法 3.通过send方式来启动 1.概念 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占 ...

  7. 使用docker-maven-plugin打包

    今天在部署的时候遇到点问题,总结一下,docker部署的步骤,如果对您有帮助,关注一下,就是对我最大的肯定, 谢谢! 微服务部署有两种方法: (1)手动部署:首先基于源码打包生成jar包(或war包) ...

  8. DRF使用超链接API实现真正RESTful

    很多API并不是真正的实现了RESTful,而应该叫做RPC (Remote Procedure Call 远程过程调用),Roy Fielding曾经提到了它们的区别,原文如下: I am gett ...

  9. python SQLAlchemy反射生成models

    1.安装SQLAcodegen pip install sqlacodegen 2.使用sqlacodegen生成案列 sqlacodegen mysql://root:123456@127.0.0. ...

  10. C#中RDLC报表中日期显示格式

    转换为日期类型再格式化 =CDate(Fields!UseDate.Value).ToString("yyyy-MM-dd") 使用Format ==Format(Fields!C ...