2017-01-30 5 views
4

counter周りにクロージャを作成するコードのこの部分を考えてみましょう:クロージャがあるときにインターロックを使用する必要がありますか?

uint counter = 0xFFFF; 
someList.AsParallel().ForEach(a => { uint tmp = ++counter }); 

(一瞬のために取って並列foreachの中にカウンタを使用することの明らかな問題を設定してください)。

tmpは0x0000または0x1FFFFになりますか?

私の推論:counterを0xFFFFから0x10000に増やすには、少なくとも2バイトのCPU命令が必要で、それはマルチスレッドで中断する可能性があります。中断された場合、counterの1バイトだけが更新される可能性があります。一時的に0x00000または0x1FFFFに設定することができます。

uint counter = 0xFFFF; 
someList.AsParallel().ForEach(a => { uint tmp = Interlocked.Increment(counter) }); 

...:

私はこのように書かれているでしょうか?

私がAsParallelを取り除くと、私は完全に安全ですか?

答えて

2

はい、Interlocked.Incrementが必要です。クロージャは、この操作がスレッドセーフではないという事実を変更しません。クロージャが行うことは、ラムダ式を表示クラスに持ち上げ、カウンタをインクリメントする複数のスレッドを引き起こす各繰り返しの同じクラスを再利用することです。

public class C 
{ 
    [CompilerGenerated] 
    private sealed class <>c__DisplayClass0_0 
    { 
     public uint counter; 
     internal void <M>b__0(int a) 
     { 
      uint num = this.counter + 1u; 
      this.counter = num; 
     } 
    } 
    public void M() 
    { 
     C.<>c__DisplayClass0_0 <>c__DisplayClass0_ = new C.<>c__DisplayClass0_0(); 
     <>c__DisplayClass0_.counter = 65535u; 
     List<int> source = new List<int> { 
      1, 
      2, 
      3 
     }; 
     source.AsParallel<int>().ForAll(new Action<int>(<>c__DisplayClass0_.<M>b__0)); 
    } 
} 

そして私はAsParallelを取り除く場合は、私は完全に安全だ:

逆コンパイルは、次のようになりますか?

反復処理中にリストまたはカウンタが変更されていない限り、大丈夫です。あなたの例から、あなたが作業しているデータの実際の場所を知ることはできませんが、すべてがメソッドスコープのローカルであると仮定すれば、あなたはうまくいくでしょう。

0

はい。並列は、マルチスレッド化のための文法的な砂糖です。スレッドセーフである必要があります。シングルスレッドの場合は、明らかにインターロックを必要としません(またはスレッドセーフにする)。

関連する問題