C# Channel

命名空间:System.Threading.Channels

Channel这个概念和C#的async stream有很多关联。可以把Channel看作是一个消息管道或者生产者/订阅者模型。生产者将消息放入Channel的一端,而订阅者/消费者从另一端消费消息。

Channel可以保证消息的顺序性。

Channel分为限容Channel(BoundedChannel)和不限容Channel(UnBundedChannel)。Channel主要通过自身的静态方法进行实例化:

var channel = Channel.CreateUnbounded<int>(); //不限容Channel
var channel=Channel.CreateBounded<int>();     //限容Channel

在Channel中,生产者也叫做Writer,消费者就是Reader,我们来看一下这个例子:

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();

上述例子创建了两个线程来模拟生产者和消费者。

消费者的“订阅”主要是由以下几种方式来进行的:

1、Channel.Reader.WaitToReadAsync()。这个方法会在当有数据可用时返回true

2、Channel.Reader.ReadAllAsync()。注意这个方法返回的是一个IAsyncEnumerable。IAsyncEnumerable是C#8推出的一种异步流模型。我们可以使用await foreach这种方式来消费异步流。在Channel中,如果Writer没有明确Complete,那么await foreach也会成为一个死循环。所以在使用这个模型前,需要注意。

3、Channel.Reader.ReadAsync()。

生产者的“生产”主要是由以下几种方式来进行的:

1、Channel.Writer.WriteAsync(),生产一条消息。

2、Channel.Writer.WaitToWriteAsync()。当有空间可写时,返回一个true。因为Channel有限容类型的Channel,所以这个方法也可以作为一个屏障,当Channel空间已满时,进行阻塞。

3、Channel.Writer.Complete()。发送一个信号,告知Reader消费者,Channel已经不会再发送任何消息了。此时Channel的状态变为closed,如果Reader继续读取信息,则会抛异常:

System.Threading.Channels.ChannelClosedException:“The channel has been closed.”

 

 
posted @ 2022-07-08 09:19  你也很优秀  阅读(1670)  评论(0编辑  收藏  举报