使用任务并行库同时处理n个项目

时间:2011-08-04 18:47:27

标签: .net multithreading concurrency parallel-processing task-parallel-library

这一切都发生在Windows服务中。

我有Queue<T>(实际上是ConcurrentQueue<T>)等待处理的项目。但是,我不想一次只处理一个,我想同时处理n个项目,其中n是一个可配置的整数。

如何使用任务并行库进行此操作?

我知道TPL会代表开发人员对集合进行并发处理,但不确定这是否是我所追求的功能。我是多线程和TPL的新手。

3 个答案:

答案 0 :(得分:4)

使用BlockingCollection<T>代替ConcurrentQueue<T>,然后您可以启动任意数量的使用者主题并使用Take的{​​{1}}方法。如果集合为空,BlockingCollection方法将自动在调用程序线程中阻塞,等待添加项,否则线程将并行使用所有队列项。但是,由于您的问题提到了使用TPL,因此Take使用Parallel.ForEach检查this帖子时会遇到一些问题,以获取更多详细信息。所以你必须管理自己的消费者线程的创建。 BlockingCollectionnew Thread(/*consumer method*/) ...

答案 1 :(得分:4)

这是一个涉及为TaskFactory创建扩展方法的想法。

public static class TaskFactoryExtension
{
    public static Task StartNew(this TaskFactory target, Action action, int parallelism)
    {
        var tasks = new Task[parallelism];
        for (int i = 0; i < parallelism; i++)
        {
            tasks[i] = target.StartNew(action);
        }
        return target.StartNew(() => Task.WaitAll(tasks));
    }
}

然后你的调用代码如下所示。

ConcurrentQueue<T> queue = GetQueue();
int n = GetDegreeOfParallelism();
var task = Task.Factory.StartNew(
  () =>
  {
    T item;
    while (queue.TryDequeue(out item))
    {
      ProcessItem(item);
    }
  }, n);
task.Wait(); // Optionally wait for everything to finish.

这是使用Parallel.ForEach的另一个想法。这种方法的问题在于您的并行度可能不一定得到尊重。您只是指出允许的最大金额而不是绝对金额。

ConcurrentQueue<T> queue = GetQueue();
int n = GetDegreeOfParallelism();
Parallel.ForEach(queue, new ParallelOptions { MaxDegreeOfParallelism = n },
  (item) =>
  {
    ProcessItem(item);    
  });

答案 2 :(得分:1)

我还建议您使用BlockingCollection而不是直接使用ConcurrentQueue

以下是一个例子:

public class QueuingRequestProcessor
{
  private BlockingCollection<MyRequestType> queue;

  public void QueuingRequestProcessor(int maxConcurrent)
  {
    this.queue = new BlockingCollection<MyRequestType>(maxConcurrent);

    Task[] consumers = new Task[maxConcurrent];

    for (int i = 0; i < maxConcurrent; i++)
    {
      consumers[i] = Task.Factory.StartNew(() =>
      {
        // Will wait when queue is empty, until CompleteAdding() is called
        foreach (var request in this.queue.GetConsumingEnumerable())
        {
          Process(request);
        }
      });
    }
  }

  public void Add(MyRequest request)
  {
    this.queue.Add(request);
  }

  public void Stop()
  {
    this.queue.CompleteAdding();
  }

  private void Process(MyRequestType request)
  {
    // Do your processing here
  }
}

请注意,构造函数中的maxConcurrent定义了将同时处理多少个请求。