前言:
当前兄弟们对“net博客科普”大体比较珍视,小伙伴们都需要剖析一些“net博客科普”的相关内容。那么小编也在网摘上汇集了一些关于“net博客科普””的相关知识,希望我们能喜欢,咱们快快来了解一下吧!1、迷茫
当一个新概念出来的时候,你很想使用它们,但是又没办法直接理解它,这是一件痛苦的事情。
对于通道,我想我遇到了麻烦!我最近一直在熟悉.NET Core 3.X中引入的Channel类型。但是有关文章非常非常少,我不能理解它们与其他队列有什么不同。
在使用了一段时间后,我终于看到了它们巨大的吸引力和真正的力量。
最值得需要关注通道的是大型异步后台操作,这些操作几乎都需要双向通信来同步它们正在做的事情。如果你们看完本系列,你会清楚我所言不虚,你也应该能学到什么时候使用Channel<T>,什么时候使用一些更基本的东西,比如Queue<T>。
2、什么是Channel
首先,Channel本质上是.net中的一种新的集合类型,它与现有的Queue<T>类型非常相似,当然也有不同之处。
在真正尝试研究这个主题时,我发现的问题是,许多现有的外部队列技术(IBM MQ、Rabbit MQ等)都有“channel”的概念,它们的范围从完全抽象的思维过程,到系统中实际的物理类型。
下面图示是rabbitmq中的通道概念,其基于tcp链接之上,为了节约和共享tcp链接,而抽象出的一个通信概念。
但是如果您将.NET中的Channel视为只是一个队列,并且包含一些其他逻辑以使其能等待新消息,并能告知生产者,该队列很繁忙,并且提供了强大的线程安全支持,这样子的理解看起来也没啥错的。
这里我提到了一个关键词,生产者/消费者。你可能还听说过Pub/Sub(发布订阅),这两者之间是不同的!。
Pub/Sub描述的是某人发布信息,一个或多个“订阅者”监听该信息并对其采取一定的响应行为。这里不存在负载平衡,因为当添加订阅服务器时,它们是所有人获得相同消息的副本,下面看看他们的不同之处。
在图表形式中,Pub/Sub看起来有点像这样:
生产者/消费者描述生产者发布消息的行为,并且有一个或多个消费者可以对该消息进行操作,但是每个消息只读取一次。它不会分发到每个订阅者。
当然,用图表的形式:
哈哈,应该讲清楚了吧,原作者xxx的废话被删掉若干…。
这通常被称为生产者-消费者问题,这是Channel要解决的问题。
3、Channel示例
与Channel有关的类都在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); }}123456789101112131415
这里很Easy。
我们创建了一个“无限的”通道(这意味着它可以容纳无限项)。我们写10项,读10项,在这一点上,它与我们在.net中见过的任何其他队列没有太大区别。
4、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); }}12345678910111213141516171819
在这里,我们有一个单独的线程写入消息,而我们的主线程读取消息。
你会注意到有趣的事是,我们添加了延迟。
我们直接调用ReadAsync(),根本没有使用类似TryDequeue或Dequeue这样的判断操作,那如果队列中没有消息,它会返回null,是吗?
揭晓答案!
Channel Reader 的“ReadAsync()”方法实际上会“等待”一个消息(但是不是阻塞)。
所以,你不需要为了等待消息而做一些荒谬的循环,也不需要在为等待消息而完全阻塞线程。
我们将在以后的文章中进一步讨论这个问题,但是你要知道你可以使用ReadAsync来等待新的消息,而不是编写一些自定义的代码来做同样的事情。
5、接下来是什么?
现在你已经掌握了基础知识,下一篇让我们看看使用Channel一些更高级的场景。
原文地址:
标签: #net博客科普