您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關C#執行順序與語句編寫順序不符的案例分析的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。
前言
編寫程序的時候,人們的直觀感覺通常認為,程序的執行順序是按照語句的順序進行的。然而,許多編程語言的規范是允許實際執行順序與語句編寫順序不符的。實際上,編譯器為了完成某種優化,常常會對一些操作進行適當的順序調整,導致一些預料之外的現象。
實驗現象
首先,通過一個例子來展示這個現象。在一個C# .NET Core 3.1命令行程序中,定義兩個全局變量a和b,在線程1中,依次對b和a進行遞增。這樣,在任何時刻b應當等于a或a+1。
static int a = 0; static int b = 0; static void Thread1() { while (true) { ++b; ++a; } }
在線程2中,先讀取a的值,然后執行一些其他操作,再讀取b的值。如果語句一定是按順序執行的,那么讀取到的b的值應當比讀取到的a的值更新,從而b必然大于或等于a(除非b發生了溢出)。編寫程序,當b < a時輸出它們的值。
static int c = 0; static void Thread2() { while (true) { c += b; var localA = a; c += b; var localB = b; if (localA > localB) { Console.WriteLine($"a={localA} b={localB}"); } } }
再編寫主程序,啟動上述的兩個線程。
static void Main(string[] args) { Task.Run(Thread1); Task.Run(Thread2); Console.ReadKey(); }
使用Debug配置,編譯并運行該程序,命令行是沒有輸出的,符合我們的預期。但是使用Release配置的話,就會出現大量輸出,其中a的值比b大1到5不等。
查看反匯編可以看到,在第1個c += b語句處,程序將b的值放到了寄存器中,而后面的語句均使用了該寄存器內存放的值。所以,編譯器實際上將對b的讀取操作合并并且前置了。以下為反匯編結果片段。
00007FFB628A394D mov rcx,7FFB6292FBD0h 00007FFB628A3957 mov edx,1 00007FFB628A395C call 00007FFBC2387B10 00007FFB628A3961 mov esi,dword ptr [7FFB6292FC08h] 00007FFB628A3967 mov ecx,esi 00007FFB628A3969 add ecx,dword ptr [7FFB6292FC0Ch] 00007FFB628A396F mov dword ptr [7FFB6292FC0Ch],ecx var localA = a; 00007FFB628A3975 mov edi,dword ptr [7FFB6292FC04h] c += b; 00007FFB628A397B add ecx,esi c += b; 00007FFB628A397D mov dword ptr [7FFB6292FC0Ch],ecx if (localA > localB) 00007FFB628A3983 cmp edi,esi 00007FFB628A3985 jle 00007FFB628A394D
理論分析
在C#語言標準的Basic concepts一章Execution order一節(參見:Basic concepts – C# language specification)中,提到了C#的執行順序規范。C#程序的副作用在以下關鍵點處的順序是被保留的:
C#程序的執行順序在滿足以下條件的情況下,可以由執行環境任意調整的:
而上述的副作用包括:
由此可以推出,C#程序中對非volatile變量的讀取順序可能會被調整。在只有一個線程對該變量進行操作時,這個順序的調整是保證不會影響結果的;但如果同時有其他的線程正在對變量進行修改,則讀取的順序是無法確定的。
因此,如果有多個線程同時訪問的,對值的實時性有要求的變量,應當設置為volatile變量。將上述實驗中的靜態變量a和b改為volatile變量后,即使是Release配置下,也不會出現命令行的輸出,即兩個變量的讀取順序符合原始的語句順序。
結論
在C#程序中,讀取非volatile變量的順序可能被執行環境任意調整。如果某個變量在被讀取的時候會被其他線程寫入,為了該讀取結果的實時性,應當將該變量設置為volatile變量。
感謝各位的閱讀!關于C#執行順序與語句編寫順序不符的案例分析就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。