天天看點

C# volatile關鍵字

本文僅為個人參考衆多文章後的了解,如有錯誤請指正,紅色内容必看。

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可用于以下類型的字段
    1. 引用類型
    2. 指針類型(C#語言支援指針類型,在不安全的上下文中)
    3. 類型為sbyte、byte、short、ushort、int、uint、char、float和bool,并非是值類型。
    4. 已知為引用類型的泛型類型參數。
    5. 基類為byte、sbyte、short、ushort、int 或 uint的枚舉類型(枚舉預設繼承自int類型,但可以枚舉設定其基類)。
    6. 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。

線程安全

原子性、可見性、有序性。

原子性:提供了互斥通路,同一時刻隻能有一個線程來對它進行操作。

可見性:一個線程對主記憶體的修改可以及時被其他線程觀察到。

有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序的存在,該觀察結果一般雜亂無序。

繼續閱讀