我有一个C#程序,它将一组工作项排队。对于每个工作项,我生成另一个应用程序(我无法更改)来处理该项目。此过程相对CPU密集。我想限制此应用程序的实例数。
我考虑过使用PLINQ:
Parallel.ForEach(
workItems,
new ParallelOptions { MaxDegreeOfParallelism = 4 },
x => Process.Start("worker", x).WaitForExit());
但我担心的是,每个并行工作者只会使用一个线程来等待相应的进程。
我还看到PLINQ尝试批处理返回的项目,这意味着它可能会在等待适当大小的批处理时停顿。那么,使用BlockingCollection
的单生产者/多消费者模式可能会起作用吗?问题是我每个并行工作者都有一个线程,这会比(PL)Q解决方案更差。
鉴于以上内容已经简化并且我实际上有一个TaskCompletionSource
连接到每个工作进程(通过Exited
事件),我是否可以使用TPL中的某些内容来执行此操作而不会阻止任何背景线程?
答案 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());
}
这给了我一个线程,可以同时保留有限数量的后台进程。