首页 > 技术文章 > 使用 .Net Core Channels 的多线程生产者消费者

DomoYao 2022-03-21 13:49 原文

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

 

首先我创建了一个通道,有两个选项 - 有界和无界,有界通道提供了更多控制,因为您可以设置通道可以承载的消息总数,因此您的生产者不会超载消费者。

 

推荐阅读