想象一下要处理的一长串数据。处理受CPU限制,可以并行完成。
要处理数据项,需要一个大对象(~50MB)来保存中间处理结果。可以在后续任务的处理期间重用该对象。
我想做这样的事情:
Processor[] processors = GetProcessors(Environment.ProcessorCount);
Parallel.For(
0,
itemCount,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
item =>
{
int threadIndex = /* TODO */;
processors[threadIndex].Process(item);
}
);
目标是只拥有我的大对象的Environment.ProcessorCount
个实例,并尽可能高效地重用它们。
我该怎么做?
答案 0 :(得分:2)
你需要use the overload of Parallel.For
接受这两个函数来设置和拆除你的线程本地对象。
Parallel.For(
0,
itemCount,
() => new Processor(),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
(item, loopState, processor) =>
{
processor.Process(item);
// return the processor to be used for another invocation
return processor;
}
processor =>
{
//Do any tear down work you need to do, like dispose the object if it is disposeable
processor.Dispose();
}
);
因为Parallel
类函数不会立即跳转到使用ParallelOptions.MaxDegreeOfParallelism
个线程(它们从一个线程开始然后逐渐增加到你定义的最大值),它只会创建一个{{1}个实例如果只创建了一个线程,并且同时创建了最多Processor
个对象。
我不知道默认调度程序的实现细节,但它可能会或可能不会停止线程然后创建新线程,从而导致创建新的ParallelOptions.MaxDegreeOfParallelism
对象。但是,如果发生这种情况(可能不会,我不知道),您仍然只能同时存在最多Processor
个对象。
答案 1 :(得分:1)
这是一种有效的方法。我在写这个问题时设计了这个答案,但认为这个问题很有趣,无论如何都要发布。如果有人有更好的解决方案,我想学习它。
使用并发集合(例如ConcurrentQueue<Processor>
)在线程之间分配Processor
的实例。
Processor[] processors = GetProcessors(Environment.ProcessorCount);
var queue = new ConcurrentQueue<Processor>(processors);
Parallel.For(
0,
itemCount,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
item =>
{
// Obtain the processor
Processor processor;
queue.TryDequeue(out processor);
processor.Process(item);
// Store the processor again for another invocation
queue.Enqueue(processor);
}
);
实际实现应声明TryDequeue
返回true,并且在异常的情况下再次将处理器排入队列。
只要处理时间远远大于队列争用所花费的时间,开销就应该是最小的。