使用嵌套的Parallel.For
考虑这个例子:
var x = 0; for (var i = 0; i < 100; i++ ) { for (var a = i+1; a < 100; a++) x += 1; }
打印x时,我们总是得到4950.如果我要将其并行化怎么办?
这就是我想出的
Parallel.For(0, 100, i => Parallel.For(i + 1, 100, a => { x += 1; }));
但是,每次运行时都不打印4950。 为什么?
Parallel Extensions以近乎迫切的语法帮助您创建任务,分配,运行和集合。 它没有做的是照顾各种线程安全( 陷阱之一 )。 您正在尝试使并行线程同时更新单个共享变量。 要正确地做这样的事情,你必须引入例如锁定。
我不确定你要做什么。 我假设您的代码只是一个占位符或实验。 并行化仅适用于您可以隔离不同的工作; 不是当你经常不得不与共享数据会合时。
这将是“正确”的方法,这不需要你锁定你的最终总对象,只需要你在每个本地线程的循环结束时进行互锁操作。
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
但是,有两个嵌套的Parallel
语句做这么少的工作可能是一个坏主意。 需要考虑开销成本,如果您正在进行如此少量的工作,那么仅执行一个Parallel
语句或者根本不进行任何操作会更好。
我强烈建议你去下载并阅读并行编程模式 ,它详细介绍了为什么这样的小嵌套并行循环不是一个好主意。
作为每次锁定的替代方法,您可以将线程局部变量与锁结合使用:
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);