2017-01-30 42 views
4

考慮這段代碼周圍counter創建一個封閉:閉合時是否需要互鎖使用?

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

(請預留了一下使用並行的foreach內的計數器的明顯的問題)。

tmp曾計算爲0x0000或0x1FFFF?

我的理由:爲了從0xFFFF增加counter到0x10000的要求至少是可以通過多線程中斷兩個字節的CPU指令。如果被中斷,有可能只有一個字節的counter將被更新 - 它可能暫時被設置爲0x00000或0x1FFFF。

如果我寫了這個爲:

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

...?

如果我擺脫AsParallel,我是否完全安全?

回答

2

是的,你需要Interlocked.Increment,閉包不會改變這個操作不是線程安全的事實。什麼閉包會把你的lambda表達式提升到一個顯示類,並在每次迭代中重複使用相同的類,這將導致多個線程遞增計數器。

反編譯如下:

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

是。 Parallel只是多線程的語法糖。你仍然需要線程安全。如果你是單線程的,你顯然不需要互鎖(或者線程安全)。

相關問題