使用 .Net Core Channels 的多线程生产者消费者
Dotnet Core 最近引入了System.Threading.Channels
var channel = Channel.CreateBounded<ChannelDataDTO>(channelLimit);
然后我为生产者和消费者制定了两项基本任务
Task producer = Task.Factory.StartNew(() = >{ //在此处添加数据生产者逻辑 channel.Writer.TryWrite(channelData); channel.Writer.Complete(); }); Task Consumer = Task.Factory.StartNew(() = >{ while (await channel.Reader.WaitToReadAsync()) { if (channel.Reader.TryRead(out var channelData)) { // 在此处添加数据消费者逻辑 } } } );
使用TryWrite可以将数据写入通道,同时确保通道尚未关闭。写入所有数据后,我们可以使用通道写入完成方法,这就是我们通知通道没有更多数据要生成的方式。
在消费者上,我们有一个WaitToReadAsync条件,它监视通道中的任何可用数据。一旦我们有了数据,类似于TryWrite我们可以使用TryRead读取它。如果我们确定生产者已停止生产,还有一个可用的ReadAllAsync方法可以读取通道上存在的所有数据。
一旦我们有了基本的生产者和消费者设置,现在有趣的部分是通过并行运行多个生产者和消费者来实现多线程,所有这些都在同一个通道中推送和拉取数据。
我使用这样的Parallel.ForEach循环启动生产者的多个线程,同时使用MaxDegreeOfParallelism控制线程数
Task producer = Task.Factory.StartNew(() = >{ Parallel.ForEach(dataToProduce, new ParallelOptions { MaxDegreeOfParallelism = Int32.Parse(threadCount) }, data = >{ // add producer logic here dataItems.Writer.TryWrite(channelData); }); dataItems.Writer.Complete(); });
Task[] Consumer = new Task[Int32.Parse(threadCount)]; for (int i = 0; i < Consumer.Length; i++) { Consumer[i] = Task.Factory.StartNew(async() = >{ while (await channel.Reader.WaitToReadAsync()) { if (channel.Reader.TryRead(out var channelData)) { // add consumer logic here } } }); }
producer.Wait();
Task.WaitAll(Consumer);
首先我创建了一个通道,有两个选项 - 有界和无界,有界通道提供了更多控制,因为您可以设置通道可以承载的消息总数,因此您的生产者不会超载消费者。