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は、ほぼ必須の構文で、タスクの作成、割り当て、実行、およびランデブーを支援します。それがしないのは、あらゆる種類のスレッドセーフ(落とし穴の1つ)に注意を払うことです。並列スレッドが単一の共有変数を同時に更新するようにしようとしています。このようなことを正しく行うには、たとえばロックを導入する必要があります。

あなたが何をしようとしているのかわかりません。あなたのコードは単なるプレースホルダーまたは実験だと思います。並列化は、さまざまな作業を分離できる場合にのみ適しています。共有データと常にランデブーする必要がある場合ではありません。

于 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ただし、この小さな作業を2つのネストされたステートメントで実行することは、おそらく悪い考えです。考慮する必要のあるオーバーヘッドコストがあります。このような少量の作業を行う場合は、Parallelステートメントを1つだけ実行するか、まったく実行しない方がはるかに優れています。

並列プログラミングのパターンをダウンロードして読むことを強くお勧めします。このような小さなネストされた並列ループが適切でない理由について詳しく説明します。

于 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 に答える