在My Parallel.ForEach循环中,对所有线程调用localFinally委托。 我发现这是因为我的并行循环失速。 在我的并行循环中,我有大约三个条件检查阶段在循环完成之前返回。而且似乎是从这些阶段返回Threads而不是整个body的执行它不执行localFinally委托。
循环结构如下:
var startingThread = Thread.CurrentThread;
Parallel.ForEach(fullList, opt,
()=> new MultipleValues(),
(item, loopState, index, loop) =>
{
if (cond 1)
return loop;
if (cond 2)
{
process(item);
return loop;
}
if (cond 3)
return loop;
Do Work(item);
return loop;
},
partial =>
{
Log State of startingThread and threads
} );
我在一个小数据集上运行循环并详细记录,发现当Parallel.ForEach完成所有迭代并且localFinally的最后一个线程的Log是 - 对于线程6循环Indx 16,调用线程状态为WaitSleepJoin 循环仍然没有优雅地完成并且仍然停滞......为什么这些摊位的线索?
干杯!
答案 0 :(得分:1)
在看到localFinally的定义(在每个线程完成后执行)之后做了一个快速测试运行,这让我怀疑这可能意味着并行性创建的线程比执行的循环少得多。 e.g。
var test = new List<List<string>> ();
for (int i = 0; i < 1000; i++)
{
test.Add(null);
}
int finalcount = 0;
int itemcount = 0;
int loopcount = 0;
Parallel.ForEach(test, () => new List<string>(),
(item, loopState, index, loop) =>
{
Interlocked.Increment(ref loopcount);
loop.Add("a");
//Thread.Sleep(100);
return loop;
},
l =>
{
Interlocked.Add(ref itemcount, l.Count);
Interlocked.Increment(ref finalcount);
});
在此循环结束时,itemcount和loopcount按预期为1000,并且(在我的机器上)finalcount为1或2,具体取决于执行速度。在具有条件的情况下:当直接返回时,执行可能更快并且不需要额外的线程。只有在执行dowork时才需要更多的线程。但是参数(在我的情况下为l)包含所有执行的组合列表。 这可能是记录差异的原因吗?
答案 1 :(得分:1)
我认为你误解了localFinally
的含义。它不是针对每个项目调用的,而是为Parallel.ForEach()
使用的每个线程调用它。许多项目可以共享同一个帖子。
它存在的原因是你可以在每个线程上独立执行一些聚合,并且最后只将它们连接在一起。这样,您只需要在一小段代码中处理同步(并使其影响性能)。
例如,如果您想计算一组项目的得分总和,您可以这样做:
int totalSum = 0;
Parallel.ForEach(
collection, item => Interlocked.Add(ref totalSum, ComputeScore(item)));
但是在这里,你为每个项目调用Interlocked.Add()
,这可能很慢。使用localInit
和localFinally
,您可以像这样重写代码:
int totalSum = 0;
Parallel.ForEach(
collection,
() => 0,
(item, state, localSum) => localSum + ComputeScore(item),
localSum => Interlocked.Add(ref totalSum, localSum));
请注意,代码仅在Interlocked.Add()
中使用localFinally
,并且访问body
中的全局状态。这样,同步成本只需支付几次,每次使用的线程一次。
注意:我在这个例子中使用了Interlocked
,因为它非常简单且非常明显正确。如果代码更复杂,我会首先使用lock
,并且只有在有必要获得良好性能时才尝试使用Interlocked
。