2016-03-10 63 views
0

我有此代碼意外的結果使用互鎖

int j = -1; 
Parallel.For(0, 100, i => 
{ 
    Console.Write("i= " +i); 
    Interlocked.Increment(ref j); 
    Console.Write(" j= " + j); 
    Console.WriteLine(); 
}); 

它運行後,我期待具有i值某種程度上隨機顯示,而不是在升序排列,但對於j可變我的值希望一直按升序排列。

所以現在我有這個輸出在某個時刻:

enter image description here

因此,有一個價值觀9394之間j= 78,這是正確的?請問你們有些人可以解釋爲什麼我在這裏有這個值,而不是7779之間,這是因爲Console.Write方法不是線程安全的嗎?

是否j變量的值給出升序(我期待),輸出j = 78只是一個小故障Console.Write方法,因爲它不是線程安全的?

+1

打印時,您的代碼中未使用任何鎖。你真正的問題是什麼? – Nayuki

+3

你應該真的使用/打印'Interlocked _return value_'。Increment',以獲得真正的原子增量,因爲'j'可以在您使用它時由另一個線程更改,而返回值在返回後不會更改。 –

+0

@JoachimIsaksson這樣做意味着每個值只能打印一次,沒有任何重複或跳過的數字,但不能保證它們按順序打印。 – Servy

回答

6

對於「j」變量的值,我期望將它們始終按升序排列。

這不僅不能保證,它甚至不保證打印每一個數字只是一次。它可以打印多次,或根本不打印。一個線程可以遞增數字(假設從0到1),然後暫停,讓另一個線程運行,然後可以再次遞增數字(至2),打印它,然後暫停,讓第一個線程恢復,然後打印相同的值(2,在這種情況下)。同樣,一個線程可以將該變量解析爲一個值,然後暫停,讓另一個線程增加數十次,隨着打印的進行,然後讓原始線程繼續,打印它從變量long拉出的值在所有這些增量之前。

從根本上說,您的增量變量和打印值不是原子操作。您需要明確同步該代碼塊,以防止多個線程同時處於這兩個操作的中間,以實現所描述的行爲。

1

只有Interlocked.Increment保證是沒有兩個線程將同時增加j。除此之外,無法確定哪個線程會先寫入控制檯。 它看起來像你在試驗,序列對你來說並不重要。這很好,因爲這是多線程應用程序如何以非確定性方式運行的一個很好的例子。在這種情況下,如果您希望發生這些事情,那麼它就會運行良好,如果您希望它們以可預測的順序發生,則不會發生這種情況。

這不會解決i,但你可以嘗試

var k = Interlocked.Increment(ref j); 
Console.Write(" k= " + k); 
Console.WriteLine(); 

這仍然不能保證順序,但它應該讓你所有的獨特價值。