使用并行for循环的线程本地资源

时间:2013-12-03 13:31:18

标签: c# .net task-parallel-library

想象一下要处理的一长串数据。处理受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个实例,并尽可能高效地重用它们。

我该怎么做?

2 个答案:

答案 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,并且在异常的情况下再次将处理器排入队列。

只要处理时间远远大于队列争用所花费的时间,开销就应该是最小的。