限制并发产生和等待工作进程

时间:2013-03-31 18:25:45

标签: c# task-parallel-library

我有一个C#程序,它将一组工作项排队。对于每个工作项,我生成另一个应用程序(我无法更改)来处理该项目。此过程相对CPU密集。我想限制此应用程序的实例数。

我考虑过使用PLINQ:

Parallel.ForEach(
    workItems,
    new ParallelOptions { MaxDegreeOfParallelism = 4 },
    x => Process.Start("worker", x).WaitForExit());

但我担心的是,每个并行工作者只会使用一个线程来等待相应的进程。

我还看到PLINQ尝试批处理返回的项目,这意味着它可能会在等待适当大小的批处理时停顿。那么,使用BlockingCollection的单生产者/多消费者模式可能会起作用吗?问题是我每个并行工作者都有一个线程,这会比(PL)Q解决方案更差。

鉴于以上内容已经简化并且我实际上有一个TaskCompletionSource连接到每个工作进程(通过Exited事件),我是否可以使用TPL中的某些内容来执行此操作而不会阻止任何背景线程?

2 个答案:

答案 0 :(得分:1)

如果您可以使用Process中的Process.Exited事件包裹Task开始和结束:

Task WrapExternalProcess( WorkItem workItem ) { ... }

您可以使用continuation完全删除阻止。这样的事情可以做到:

Task DoAllWork( IEnumerable<WorkItem> workItems )
{
  int THREAD_COUNT = 4;

  var bag = new ConcurrentBag<WorkItem>( workItems );
  var ce = new CountdownEvent( THREAD_COUNT );
  var tcs = new TaskCompletionSource<bool>();

  for ( int i = 0 ; i < THREAD_COUNT ; i++ ) Work( bag, ce, tcs );

  return tcs.Task;
}

void Work(
  ConcurrentBag<WorkItem> bag,
  CountdownEvent ce,
  TaskCompletionSource<bool> tcs
)
{
    WorkItem workItem;
    if ( bag.TryTake( out workItem) )
    {
        WrapExternalProcess( workItem )
          .ContinueWith( t => Work( bag, ce, tcs ) );
    }
    else // no more work
    {
        // If I'm the last thread to finish
        if ( ce.Signal() ) tcs.SetResult( true );
    }
}

答案 1 :(得分:0)

我已经实现了以下内容:

var sem = new SemaphoreSlim(4);
foreach (var item in workItems)
{
    sem.Wait();

    ProcessAsync(item).ContinueWith(_ => sem.Release());
}

这给了我一个线程,可以同时保留有限数量的后台进程。