2017-05-24 64 views
1

Joe Albahari在C#中解釋了volatile的一篇很好的文章:Threading in C#: PART 4: ADVANCED THREADING易失性讀/寫和指令重新排序

考慮指令重新排序喬使用這個例子:

public class IfYouThinkYouUnderstandVolatile 
{ 
    private volatile int x, y; 

    private void Test1() // Executed on one thread 
    { 
    this.x = 1; // Volatile write (release-fence) 
    int a = this.y; // Volatile read (acquire-fence) 
    } 

    private void Test2() // Executed on another thread 
    { 
    this.y = 1; // Volatile write (release-fence) 
    int b = this.x; // Volatile read (acquire-fence) 
    } 
} 

基本上他的意思是,ab都可以用包含0時所述方法對不同的線程並行運行結束。

督察優化或處理器可能重新排序的說明如下:

public class IfYouThinkYouUnderstandVolatileReordered 
{ 
    private volatile int x, y; 

    private void Test1() // Executed on one thread 
    { 
    int tempY = this.y; // Volatile read (reordered) 
    this.x = 1; // Volatile write 
    int a = tempY; // Use the already read value 
    } 

    private void Test2() // Executed on another thread 
    { 
    int tempX = this.x; // Volatile read (reordered) 
    this.y = 1; // Volatile write (release-fence) 
    int b = tempX; // Use the already read value 
    } 
} 

的,爲什麼這會雖然我們使用volatile發生的原因是,讀指令下一個寫指令可移動在之前寫入指令

到目前爲止,我明白這裏發生了什麼。

我的問題是:可以通過堆棧幀重新排序嗎?我的意思是在一個易失性讀指令發生在另一個方法(或屬性訪問器)之後,可以將一條易失性寫指令移走嗎?

看看下面的代碼:它使用屬性而不是直接訪問實例變量。

如何在這種情況下重新排序?無論如何,它會發生嗎?或者只有當編譯器內聯訪問屬性時纔可能發生?

public class IfYouThinkYouUnderstandVolatileWithProps 
{ 
    private volatile int x, y; 

    public int PropX 
    { 
    get { return this.x; } 
    set { this.x = value; } 
    } 

    public int PropY 
    { 
    get { return this.y; } 
    set { this.y = value; } 
    } 

    private void Test1() // Executed on one thread 
    { 
    this.PropX = 1; // Volatile write (release-fence) 
    int a = this.PropY; // Volatile read (acquire-fence) 
    } 

    private void Test2() // Executed on another thread 
    { 
    this.PropY = 1; // Volatile write (release-fence) 
    int b = this.PropX; // Volatile read (acquire-fence) 
    } 
} 
+0

'volatile'不會停止所有**重新排序。使用此關鍵字相同類型的操作仍然可以重新排序(如在寫入之前的兩個讀取和在讀取之前的兩個寫入)。 – VMAtm

回答

1

你不應該考慮如此高層次的事情,因爲你無法控制它們。 JIT有很多原因可以嵌入或不嵌入。 重新排序是一個很好的概念,可以讓你推斷並行代碼執行的可能結果。但真正發生的事情不僅僅是重新排序讀寫操作。它可以通過JIT對CPU寄存器中的值進行真正的重新排序或高速緩存值,或者由CPU本身的推測執行的效果,或者內存控制器如何執行其工作。

考慮指針(或更少)大小的內存塊的讀寫。使用重新排序模型這樣的讀取和寫入,並不依賴於您的程序運行的JIT或CPU的今天的具體情況。

1

如在ECMA-335

所述

I.12.6.4優化
符合的CLI的實現是免費使用,保證任何技術來執行程序,單個執行線程內,由線程生成的副作用和異常按CIL指定的順序可見。爲此,只有易失性操作(包括易失性讀取)構成可見的副作用。 (請注意,雖然只有易失性操作構成可見的副作用,但易失性操作也會影響非易失性引用的可見性。)012.易失性操作在§I.12.6.7中規定。相對於有注入到另一個線程線程沒有例外排序保證(這些例外有時被稱爲「異步異常」 (例如,System.Threading.ThreadAbortException)。

所以,很顯然它允許內聯所有