c# 异步进阶————channel [一]
前言
该系列为异步编程的进阶篇,其实也不能这么讲。世界上本没有进阶篇,只能说是高级篇(高级篇不能说多高级,是对底层的封装的意思),只要是加深理解都是进阶。
本章先介绍一下channel。
正文
下面没什么好说的,把文档贴一下。
https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.channels?view=net-6.0
Channels 是做什么的呢?
提供用于在生成者和使用者之间以异步方式传递数据的一组同步数据结构。
这里面有同步又有异步,到底该怎么理解呢?
首先这里面有生成者和使用者两个概念。
那么怎么理解同步数据呢? 就是说使用者是按照生成者的生成数据顺序进行使用的。
然后这个异步方式传递数据是怎么回事呢? 那就是说比如生成者生产了一条消息,然后使用者使用了,然后生产者才能继续生成,那么就是同步,反之就是异步。
为什么用同步来举例,然后反之就是异步呢? 因为异步的情况太多了。
那么这里就解释完了。
然后这里要说明一点的就是有些初学者难理解这里说的channel 是一种数据结构,里面不是有方法吗?
先说下数据结构的含义:
A data structure is a storage that is used to store and organize data. It is a way of arranging data on a computer so that it can be accessed and updated efficiently.
数据结构是一种用来存储和组织数据的存储器。然后一种编排数据的方法用来访问和更新数据。
所以数据结构不仅仅是用来存储的,里面还有组织数据的能力。
比如我们的数组、集合啊,都是数据结构。 其实就两个特征,一个是存储,另外一个是组织。
然后里面有上面这些类哈。
来看下第一个类:
BoundedChannelOptions 继承自channelOptions。
那么还是先看channelOptions。
这里面除了AllowSynchronousContinuations,其他含义很清楚了,就不看了。这个AllowSynchronousContinuations 又很绕,等下实践的时候再看下。
BoundedChannelOptions 从表面意思是有边界的配置, 比 channeloptions 多了两个东西。
里面就是限制管道中的最大消息数,另一个就是如果到达消息数后,怎么处理的问题。
处理方式有很多种:
然后思考一个问题,那就是为什么要限制里面的消息数。
这里面和缓存一个道理,比如说不断的往里面加入消息数,且消费者无法消费完,然后内存就会不断的升高,因为我们的内存有限,不然做到无限缓存。
而且还有一个问题,那就是如果消费不完,然后不断往里面增加数据还会增加io成本,所以说应该考虑自己的消费情况,来设置最大消息数。
channel 提供了有边界选项,同样也提供了无边界的UnboundedChannelOptions 。
这里有人就会问了, 为啥要无边界的,上面不是说要设置有边界的,无边界不是会有问题吗?
是的,无边界的确会有问题。
这个无边界的选择意思是让你自己控制生产速度。
举个例子,比如说你需要进行对流量控制,每条消息的大小都不一样,那么这个时候你对消息数进行限制,就是达不到效果的。
这里要说明的无边界不是真的让你放飞自我。如果你肯定消费者一定能达到预期的消费,那么你也可以不限制,但是最好不要这么做。
因为channel 毕竟是单台机器的使用, 不像kafka这种集群模式的,具备很大的存储能力。仅个人建议,如有不同想法可以交流一下。
channel 这个类,就是一个构造器,创建有边界的channel 和无边界的。
创建出来的channel 有 读取器和写入器:
至于例子,在网上找了一个例子:
using System.Threading.Channels;
var channel = Channel.CreateUnbounded<int>();
Task.Run(async () =>
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(TimeSpan.FromMilliseconds(200));
await channel.Writer.WriteAsync(i);// 生产者写入消息
if (i > 5)
{
channel.Writer.Complete(); //生产者也可以明确告知消费者不会发送任何消息了
}
}
});
Task.Run(async () =>
{
await foreach (var item in channel.Reader.ReadAllAsync())//async stream,在没有被生产者明确Complete的情况下,这里会一致阻塞下去
{
Console.WriteLine(item);
}
Console.WriteLine("done");
});
Console.ReadKey();
这个例子比较简单,后面会编写一个kafka 批量消费的库,里面用到了这个channel,到时候可以交流一下。
结
该系列不断更新,主要是介绍一下一些异步编程高级部分(不是指内容多高级而是指上层应用),内容偏设计方面,实战例子会在后面的开源中体现。