队列和线程

时间:2017-02-02 16:45:35

标签: c# multithreading queue

在多个线程上需要为Queue设置Queue但在单个主线程上只需要Enqueue()时,在C#中使用的最佳Dequeue()数据结构是什么?我的线程结构如下所示:

  
      
  • 主要主题 - 消费者
  •   
  • Sub Thread1 - Producer
  •   
  • Sub Thread2 - Producer
  •   
  • Sub Thread3 - Producer
  •   

我有一个Queue<T> queue,它包含子线程和主线程调用queue.Dequeue()生成的所有项目,直到它为空。为此,我在主线程上调用了以下函数。

public void ConsumeItems()
{
    while (queue.Count > 0)
    {
         var item = queue.Dequeue();
         ...
    }
}

主线程通过每个线程循环调用此函数一次,我想确保我在线程安全的庄园中访问queue但我还想避免在可能的情况下锁定queue的原因。

1 个答案:

答案 0 :(得分:1)

您要使用的是BlockingCollection<T>,默认情况下由ConcurrentQueue<T>支持。要从队列中获取项目,您可以使用foreach内的.GetConsumingEnumerable()

public BlockingCollection<Item> queue = new BlockingCollection<Item>();

public void LoadItems()
{
    var(var item in SomeDataSource())
    {
         queue.Add(item);
    }
    queue.CompleteAdding();
}

public void ConsumeItems()
{
    foreach(var item in queue.GetConsumingEnumerable())
    {
         ...
    }
}

当队列为空时,foreach将阻止该线程,并在项目可用时立即取消阻止。一旦.CompleteAdding()被调用,foreach将完成处理队列中的任何项目,但一旦它为空,它将退出foreach块。

但是,在您执行此操作之前,我建议您查看TPL Dataflow,使用它,您不再需要管理队列或线程。它允许您构建逻辑链,并且链中的每个块都可以具有单独的并发级别。

public Task ProcessDataAsync(IEnumerable<SomeInput> input)
{
    using(var outfile = new File.OpenWrite("outfile.txt"))
    {
        //Create a convert action that uses the number of processors on the machine to create parallel blocks for processing.
        var convertBlock = new TransformBlock<SomeInput, string>(x => CpuIntensiveConversion(x), new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = Enviorment.ProcessorCount});

        //Create a single threaded action that writes out to the textwriter.
        var writeBlock = new ActionBlock<string>(x => outfile.WriteLine(x))

        //Link the convert block to the write block.
        convertBlock.LinkTo(writeBlock, new DataflowLinkOptions{PropagateCompletion = true});

        //Add items to the convert block's queue.
        foreach(var item in input)
        {
              await convertBlock.SendAsync();
        }

        //Tell the convert block we are done adding. This will tell the write block it is done processing once all items are processed.
        convertBlock.Complete();

        //Wait for the write to finish writing out to the file;
        await writeBlock.Completion;
    }
}