zoukankan      html  css  js  c++  java
  • 在.NET Core中使用Channel(二)

    在我们之前的文章中,看了一些非常简单的例子来说明Channel是如何工作的,我们看到了一些非常漂亮的特性,但大多数情况下它与其他某某Queue实现非常相似。让我们进入一些更高级的话题。我说的是高级,但其中很多都非常简单。

    读/写分离

    如果你曾经在两个类之间共享队列,你就会知道任何一个类都可以读/写,即使它们本不应该这样做。例如:

    class MyProducer
    {
        private readonly Queue<int> _queue;
    
        public MyProducer(Queue<int> queue)
        {
            _queue = queue;
        }
    }
    
    class MyConsumer
    {
        private readonly Queue<int> _queue;
    
        public MyConsumer(Queue<int> queue)
        {
            _queue = queue;
        }
    }

    因此,生产者应该只写队列,消费者应该只读队列,在这两种情况下,它们可以对队列执行所有操作。虽然你可能在自己的脑海中希望消费者只读取,但另一个开发人员可能会出现调用Enqueue,除了代码审查之外,没有什么可以阻止他们犯这个错误。

    但是有了Channel,我们可以做不同的事情。

    class Program
    {
        static async Task Main(string[] args)
        {
            var myChannel = Channel.CreateUnbounded<int>();
            var producer = new MyProducer(myChannel.Writer);
            var consumer = new MyConsumer(myChannel.Reader);
        }
    }
    
    class MyProducer
    {
        private readonly ChannelWriter<int> _channelWriter;
    
        public MyProducer(ChannelWriter<int> channelWriter)
        {
            _channelWriter = channelWriter;
        }
    }
    
    class MyConsumer
    {
        private readonly ChannelReader<int> _channelReader;
    
        public MyConsumer(ChannelReader<int> channelReader)
        {
            _channelReader = channelReader;
        }
    }

    在这个例子中,我添加了一个main方法来向你展示如何创建writer/reader,但它非常简单。这里我们可以看到,对于我们的生产者,我只传递给它一个ChannelWriter,所以它只能做写操作。对于我们的消费者,我们传递给它一个ChannelReader,所以它只能读取。

    当然,这并不意味着其他开发人员不能修改代码并开始注入根Channel对象,或者同时传入ChannelWriter/ChannelReader,但这至少比之前的情况要好得多。

    完成一个Channel

    我们在前面看到,当在通道上调用ReadAsync()时,它实际上会在那里等待消息,但是如果没有更多的消息到来呢?对于.net中的其他队列,我们通常需要传递某种共享的布尔值或一个CancellationToken。但有了Channel,就更容易了。

    考虑以下几点:

    static async Task Main(string[] args)
    {
        var myChannel = Channel.CreateUnbounded<int>();
    
        _ = Task.Factory.StartNew(async () =>
        {
            for (int i = 0; i < 10; i++)
            {
                await myChannel.Writer.WriteAsync(i);
            }
    
            myChannel.Writer.Complete();
        });
    
        try
        {
            while (true)
            {
                var item = await myChannel.Reader.ReadAsync();
                Console.WriteLine(item);
                await Task.Delay(1000);
            }
        }catch(ChannelClosedException e)
        {
            Console.WriteLine("Channel was closed!");
        }
    }

    我让第二个线程尽可能快地写入我们的Channel,然后完成它。然后我们的读取器缓慢读取,每次读取之间有1秒的延迟。注意,我们捕获了ChannelClosedExecption,当你尝试从关闭通道读取最后消息之后时将调用它。

    我只是想说清楚。在Channel上调用Complete()不会立即关闭通道并杀死读取该通道的所有人。而是通知所有服务,一旦最后一条消息被读取,我们就完成了。这很重要,因为这意味着当我们等待新条目时,当队列是空的时,当队列是满的时,是否调用Complete()都无关紧要。我们可以肯定,我们将完成所有可得到的工作。

    在Channel中使用IAsyncEnumerable

    以我们试图关闭一个Channel为例,有两件事引起了我的注意。

    我们有一个while(true)循环。这并不是真的那么糟糕,但它有点碍眼。

    为了打破这个循环,并知道Channel已经完成,我们必须捕获异常并将其吞下。

    使用命令“ReadAllAsync()”来解决这些问题,它返回一个IAsyncEnumerable。代码看起来有点像这样:

    static async Task Main(string[] args)
    {
        var myChannel = Channel.CreateUnbounded<int>();
    
        _ = Task.Factory.StartNew(async () =>
        {
            for (int i = 0; i < 10; i++)
            {
                await myChannel.Writer.WriteAsync(i);
            }
    
            myChannel.Writer.Complete();
        });
    
        await foreach(var item in myChannel.Reader.ReadAllAsync())
        {
            Console.WriteLine(item);
            await Task.Delay(1000);
        }
    }

    现在代码读起来好多了,并且去掉了捕获异常的一些多余的东西。因为我们使用的是IAsyncEnumerable,所以我们仍然可以像以前那样等待每一项,但是我们不再需要捕获异常,因为当Channel完成时,它只是简单地说没有其他东西了,然后循环退出。

    同样,这消除了在处理队列时必须编写的一些混乱代码。以前你必须编写某种无限循环,而现在它只是一个真正整洁的循环,可以处理底层的所有东西。

    接下来是什么

    到目前为止,我们一直在使用“无限的”通道。你可能已经猜到了,当然也可以选择使用BoundedChannel。查看本系列的下一部分,更好地理解这些东西。

     欢迎关注我的公众号,如果你有喜欢的外文技术文章,可以通过公众号留言推荐给我。

    原文链接:https://dotnetcoretutorials.com/2020/11/24/using-channels-in-net-core-part-2-advanced-channels/

  • 相关阅读:
    Ubuntu 设置网卡固定IP
    gawk Notes(2)
    再读simpledb 之 存储的实现
    [zZ]HDFSRAID使用Erasure Code来实现HDFS的数据冗余
    初识gawk, gawk Notes(1)
    gawk notes(3)
    Shell Notes(2)
    凶残的突击面试
    Google 图片下载工具
    Shell Notes(3)
  • 原文地址:https://www.cnblogs.com/hhhnicvscs/p/14254150.html
Copyright © 2011-2022 走看看