Using nested Parallel.For
请考虑以下示例:
1 2 3 4 5 6 7 | var x = 0; for (var i = 0; i < 100; i++ ) { for (var a = i+1; a < 100; a++) x += 1; } |
打印x时总是得到4950。如果我想并行化怎么办?
这就是我想出的
1 | Parallel.For(0, 100, i => Parallel.For(i + 1, 100, a => { x += 1; })); |
但是,每次我运行它都不会打印4950。为什么?
Parallel Extensions以几乎命令式的语法帮助您完成任务的创建,分配,运行和集合。它没有做的是照顾每种线程安全(陷阱之一)。您试图使并行线程同时更新单个共享变量。要正确执行此类操作,您必须先介绍例如锁定。
我不确定您要做什么。我假设您的代码只是一个占位符或实验。并行化仅在您可以隔离不同的工作时才适用。而不是在您经常需要共享数据的时候。
这将是执行此操作的"正确"方法,这将不需要您锁定最终的总对象,而仅需要您在每个本地线程循环结束时执行互锁操作。
1 2 3 4 5 6 7 8 9 10 11 12 | int x = 0; Parallel.For(0, 100, () => 0, //LocalInit (i, loopstate, outerlocal) => { Parallel.For(i + 1, 100, () => 0, //LocalInit (a, loopState, innerLocal) => { return innerLocal + 1; }, (innerLocal) => Interlocked.Add(ref outerlocal, innerLocal)); //Local Final return outerlocal; }, (outerLocal) => Interlocked.Add(ref x, outerLocal)); //Local Final |
但是,让两个嵌套的
我强烈建议您下载并阅读《并行编程模式》,它详细介绍了为什么这样的小嵌套并行循环不是一个好主意。
作为每次锁定的替代方法,可以将线程局部变量与锁结合使用:
1 2 3 4 5 6 7 8 9 | Object thisLock = new Object(); var globalSum = 0; System.Threading.Tasks.Parallel.For(0, 100, i => { System.Threading.Tasks.Parallel.For(i + 1, 100, () => 0, (num, loopState, subSum) => ++subSum, subSum => { lock(thisLock) { globalSum += subSum; } }); }); Console.WriteLine(globalSum); |