[C#.NET 拾遗补漏]10:理解 volatile 关键字
要理解 C# 中的?volatile?關鍵字,就要先知道編譯器背后的一個基本優化原理。比如對于下面這段代碼:
public?class?Example {public?int x;public?void?DoWork(){x = 5;var y = x + 10;Debug.WriteLine("x = " +x + ", y = " +y);} }在 Release 模式下,編譯器讀取?x = 5?后緊接著讀取?y = x + 10,在單線程思維模式下,編譯器會認為?y?的值始終都是?15。所以編譯器會把?y = x + 10?優化為?y = 15,避免每次讀取?y?都執行一次?x + 5。但?x?字段的值可能在運行時被其它的線程修改,我們拿到的?y?值并不是通過最新修改的?x?計算得來的,y?的值永遠都是?15。
也就是說,編譯器在 Release 模式下會對字段的訪問進行優化,它假定字段都是由單個線程訪問的,把與該字段相關的表達式運算結果編譯成常量緩存起來,避免每次訪問都重復運算。但這樣就可能導致其它線程修改了字段值而當前線程卻讀取不到最新的字段值。為了防止編譯器這么做,你就要讓編譯器用多線程思維去解讀代碼。告訴編譯器字段的值可能會被其它線程修改,這種情況不要使用優化策略。而要做到這一點,就需要使用?volatile?關鍵字。
給類的字段添加?volatile?關鍵字,目的是告訴編譯器該字段的值可能會被多個獨立的線程改變,不要對該字段的訪問進行優化。
使用?volatile?可以確保字段的值是可用的最新值,而且該值不會像非?volatile?字段值那樣受到緩存的影響。好的做法是將每個可能被多個線程使用的字段標記為?volatile,以防止非預期的優化行為。
為了加深理解,我們來看一個實際的例子:
public?class?Worker {private?bool _shouldStop;public?void?DoWork(){bool work = false;// 注意:這里會被編譯器優化為 while(true)while (!_shouldStop){work = !work; // do sth.}Console.WriteLine("工作線程:正在終止...");}public?void?RequestStop(){_shouldStop = true;} }public?class?Program {public?static?void?Main(){var worker = new Worker();Console.WriteLine("主線程:啟動工作線程...");var workerTask = Task.Run(worker.DoWork);// 等待 500 毫秒以確保工作線程已在執行Thread.Sleep(500);Console.WriteLine("主線程:請求終止工作線程...");worker.RequestStop();// 待待工作線程執行結束workerTask.Wait();//workerThread.Join();Console.WriteLine("主線程:工作線程已終止");} }在這個例子中,while (!_shouldStop)?會被編譯器優化為?while(true)。我們可以看一下實際的運行效果來驗證這一點。切換 Release 模式,按 Ctrl + F5 運行程序,運行效果始終如下:
程序運行后,雖然主線程在 500 毫秒后執行?RequestStop()?方法修改了?_shouldStop?的值,但工作線程始終都獲取不到?_shouldStop?最新的值,也就永遠都不會終止?while?循環。
我們修改一下程序,對?_shouldStop?字段加上?volatile?關鍵字:
public?class?Worker {private?volatile?bool _shouldStop;public?void?DoWork(){bool work = false;// 獲取的是最新的 _shouldStop 值while (!_shouldStop){work = !work; // do sth.}Console.WriteLine("工作線程:正在終止...");}// ...(略) }此時在主線程調用?RequestStop()?方法后,工作線程便立即終止了,運行效果如下圖所示:
這說明加了?volatile?關鍵字后,程序可以實時讀取到字段的最新值。
注意,一定要切換為 Release 模式運行才能看到?volatile?發揮的作用,Debug 模式下即使添加了?volatile?關鍵字,編譯器也是不會執行優化的。
當然,并不是所有的類型都可以使用?volatile?關鍵字修飾的,常見的使用?volatile?的類型是這些簡單類型:sbyte, byte, short, ushort, int, uint, char, float 和 bool,其它的請查看參考鏈接。
參考:?https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/volatile
-
精致碼農
帶你洞悉編程與架構
↑長按圖片識別二維碼關注,不要錯過網海相遇的緣分
總結
以上是生活随笔為你收集整理的[C#.NET 拾遗补漏]10:理解 volatile 关键字的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5G在工业互联网应用的机遇与挑战
- 下一篇: 跟我一起学.NetCore之Mediat