本文僅為個人參考衆多文章後的了解,如有錯誤請指正,紅色内容必看。
volatile使用:
private volatile int intValue;
private volatile List<string> strValues;
private volatile FormWindowState style;
private volatile IntPtr mainPtr;
private void button8_Click(object sender, EventArgs e)
{
lock (this)
{
intValue++;
strValues.Add("1");
style = FormWindowState.Minimized;
mainPtr = Handle;
}
}
volatile使用說明:
- volatile無法保證線程安全!!!
- volatile隻能修飾字段,不能修飾屬性、局部變量。
- volatile可用于以下類型的字段
- 引用類型
- 指針類型(C#語言支援指針類型,在不安全的上下文中)
- 類型為sbyte、byte、short、ushort、int、uint、char、float和bool,并非是值類型。
- 已知為引用類型的泛型類型參數。
- 基類為byte、sbyte、short、ushort、int 或 uint的枚舉類型(枚舉預設繼承自int類型,但可以枚舉設定其基類)。
- IntPtr和UInPtr類型。
- 多線程操作volatile修飾字段的代碼片段應該使用lock,且提供給lock的對象在不同的代碼片段中應該為同一個引用對象。
volatile原理說明:
- MSDN說明:
volatile 關鍵字訓示一個字段可以由多個同時執行的線程修改。 聲明為 volatile 的字段不受編譯器優化(假定由單個線程通路)的限制。 這樣可以確定該字段在任何時間呈現的都是最新的值。
- MSDN翻譯:
CLR為了效率會進行優化,會允許線程對變量進行本地緩存等優化操作,當線程操作完後回将變量回寫到記憶體中。當多線程同時執行時會出現變量通路不一緻情況,使用volatile可以避免這種優化,保證所有線程對變量的讀寫都在記憶體上,保證了所有處理通路該字段都是最新值。
volatile為什麼不是線程安全
MSDN上有這麼一句話:volatile 修飾符通常用于由多個線程通路但不使用 lock 語句對通路進行序列化的字段。
字段為了線程安全可以使用屬性在set和get方法中采用lock語句對字段實作線程安全,使用了volatile隻是對字段原子性和可見性進行了保證,但沒有保證線程對字段的操作性是有序性。即使在set和get方法中使用lock,依然無法保證對num的修改是線程安全的,因為對num操作的代碼依然是線程非安全的。
例如:A和B線程同時對volatile修飾的int類型變量num進行加1操作,當A線程讀取num的值後B線程讀取num的值後加1指派給num,線程A對num也加1後指派給num。雖然A和B讀取的num值都是最新的,但是對num+1的操作無法保證原子性和有序性,B先指派給num後成為num+1,A再次進行寫入依然是num+1。
線程安全
原子性、可見性、有序性。
原子性:提供了互斥通路,同一時刻隻能有一個線程來對它進行操作。
可見性:一個線程對主記憶體的修改可以及時被其他線程觀察到。
有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序的存在,該觀察結果一般雜亂無序。