7

考虑这个例子:

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。为什么?

4

3 回答 3

15

Parallel Extensions 以近乎命令式的语法帮助您创建、分配、运行和集合任务。它没有做的是照顾每一种线程安全(其中一个陷阱)。您正在尝试使并行线程同时更新单个共享变量。要正确执行此类操作,您必须引入例如锁定。

我不确定你想做什么。我假设您的代码只是一个占位符或实验。仅当您可以隔离不同的工作时,并行化才适用;不是当你经常不得不与共享数据会合时。

于 2010-07-19T11:48:26.407 回答
2

这将是执行此操作的“正确”方式,这不需要您锁定最终的总对象,并且只需要您在每个本地线程循环结束时执行互锁操作。

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语句或根本不做。

我强烈建议您下载并阅读Patterns for Parallel Programming,它详细说明了为什么像这样的小型嵌套并行循环不是一个好主意。

于 2013-10-04T18:07:19.113 回答
0

作为每次锁定的替代方法,您可以将线程局部变量与锁定结合使用:

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);
于 2010-07-19T12:12:31.197 回答