Visual Basic .NET 或 Visual Basic 可讓您第一次在 Visual Basic 應用程式中使用線程。 線程導入了競爭條件和死結等偵錯問題。 本文將探討這兩個問題。
原始產品版本: Visual Basic、Visual Basic .NET
原始 KB 編號: 317723
發生競爭條件時
當兩個線程同時存取共用變數時,就會發生競爭狀況。 第一個線程會讀取變數,而第二個線程會從變數讀取相同的值。 然後,第一個線程和第二個線程會在值上執行其作業,而且會爭先查看哪一個線程可以最後寫入共用變數的值。 最後寫入其值的線程值會保留,因為線程正在寫入先前線程所寫入的值。
競爭條件的詳細數據和範例
每個線程都會配置預先定義的一段時間,以在處理器上執行。 當為線程配置的時間到期時,線程的內容會儲存到線程下一次開啟處理器之前,處理器會開始執行下一個線程。
單行命令如何造成競爭條件
檢查下列範例,以查看競爭狀況的發生方式。 有兩個線程,兩者都在更新稱為 total 的共用變數(在元件程式代碼中以 表示 dword ptr ds:[031B49DCh]
)。
線程 1
Total = Total + val1
線程 2
Total = Total - val2
從上述 Visual Basic 程式代碼編譯的元件碼 (行號) :
線程 1
1. mov eax,dword ptr ds:[031B49DCh] 2. add eax,edi 3. jno 00000033 4. xor ecx,ecx 5. call 7611097F 6. mov dword ptr ds:[031B49DCh],eax
線程 2
1. mov eax,dword ptr ds:[031B49DCh] 2. sub eax,edi 3. jno 00000033 4. xor ecx,ecx 5. call 76110BE7 6. mov dword ptr ds:[031B49DCh],eax
藉由查看元件程式代碼,您可以看到處理器在較低層級執行的作業數,以執行簡單的加法計算。 線程可以在處理器上執行其元件程式代碼的所有或部分。 現在查看此程式代碼中發生競爭狀況的方式。
Total
是 100, val1
是 50,而 val2
是 15。 線程 1 有機會執行,但只會完成步驟 1 到 3。 這表示 Thread 1 會讀取變數並完成新增。 線程 1 現在只是等待寫出其新值 150。 停止線程 1 之後,線程 2 會完全執行。 這表示它已將計算出的值 (85) 寫入變數 Total
。 最後, 線程 1 會重新取得控制權並完成執行。 它會寫出其值 (150)。 因此,當線程 1 完成時,的值Total
現在是 150,而不是 85。
您可以看到這可能是一個主要問題。 如果這是銀行計劃,客戶會在其帳戶中擁有不應存在的錢。
此錯誤是隨機的,因為線程 1 可以在處理器到期之前完成其執行,然後 Thread 2 可以開始執行。 如果發生這些事件,則不會發生問題。 線程執行不具決定性,因此您無法控制執行的時間或順序。 另請注意,線程可能會在運行時間與偵錯模式中以不同的方式執行。 此外,您可以看到,如果您在數列中執行每個線程,則不會發生錯誤。 這種隨機性會使這些錯誤更難追蹤和偵錯。
若要防止競爭條件發生,您可以鎖定共用變數,讓一次只有一個線程可以存取共用變數。 請謹慎執行此動作,因為如果線程 1 中鎖定變數,而 Thread 2 也需要變數,則 Thread 2 的執行會停止,而 Thread 2 會等候 Thread 1 釋放變數。 (如需詳細資訊,請參閱 SyncLock
本文的 參考 一節。
競爭狀況的徵兆
競爭條件最常見的徵兆是多個線程之間共用之變數的無法預測值。 這會產生線程執行順序的不可預測性。 有時候一個線程會贏,另一個線程會贏。 在其他時間,執行正常運作。 此外,如果個別執行每個線程,變數值就會正確運作。
發生死結時
當兩個線程同時鎖定不同的變數,然後嘗試鎖定另一個線程已鎖定的變數時,就會發生死結。 因此,每個線程都會停止執行,並等候其他線程釋放變數。 因為每個線程都保留另一個線程想要的變數,因此不會發生任何動作,而且線程會保持死結狀態。
死結的詳細數據和範例
下列程式代碼有兩個 物件和 LeftVal
RightVal
:
線程 1
SyncLock LeftVal SyncLock RightVal 'Perform operations on LeftVal and RightVal that require read and write. End SyncLock End SyncLock
線程 2
SyncLock RightVal SyncLock LeftVal 'Perform operations on RightVal and LeftVal that require read and write. End SyncLock End SyncLock
當允許線程 1 鎖定 LeftVal
時,就會發生死結。 處理器會 停止 Thread 1 的執行,並開始執行 Thread 2。 線程 2 鎖定 RightVal
,然後嘗試鎖定 LeftVal
。 因為 LeftVal
已鎖定, 線程 2 會停止並等候 LeftVal
釋放。 因為 線程 2 已停止, 因此允許線程 1 繼續執行。 線程 1 嘗試鎖定 RightVal
,但無法鎖定,因為 線程 2 已鎖定它。 因此, Thread 1 會開始等候 RightVal 變成可用。 每個線程會等候另一個線程,因為每個線程都鎖定了另一個線程正在等候的變數,而且兩個線程都不會解除鎖定它所持有的變數。
死結不一定會發生。 如果 Thread 1 在處理器停止之前執行這兩個鎖定,Thread 1 可以執行其作業,然後解除鎖定共用變數。 線程 1 解除鎖定變數之後,Thread 2 可以如預期般繼續執行。
當這些代碼段並排放置時,此錯誤似乎很明顯,但實際上,程式代碼可能會出現在程式代碼的不同模組或區域中。 這是很難追蹤的錯誤,因為從這個相同的程式代碼中,可能會發生正確的執行和不正確的執行。
死結的癥狀
死結的常見徵兆是程式或線程群組停止回應。 這也稱為停止回應。 至少有兩個線程正在等候另一個線程鎖定的變數。 線程不會繼續,因為兩個線程都不會釋放其變數,直到它取得另一個變數為止。 如果程式正在等候其中一個或兩個線程完成執行,則整個程式可能會停止回應。
什麼是線程
進程可用來分隔在單一計算機上指定時間執行的不同應用程式。 操作系統不會執行進程,但線程會執行。 線程是一個執行單位。 操作系統會將處理器時間配置給線程,以執行線程的工作。 單一進程可以包含多個線程的執行。 如果線程在指派給處理器期間無法完成執行,則每個線程都會維護自己的例外狀況處理程式、排程優先順序,以及一組操作系統用來儲存線程內容的結構。 內容會保留到線程接收處理器時間的下一次為止。 內容包含線程需要順暢地繼續執行的所有資訊。 此資訊包括線程的處理器緩存器集,以及主機進程位址空間內的呼叫堆疊。
參考資料
如需詳細資訊,請搜尋Visual Studio說明以取得下列關鍵詞:
SyncLock
. 允許鎖定物件。 如果另一個線程嘗試鎖定相同的物件,則會封鎖該物件,直到第一個線程釋放為止。 請小心使用SyncLock
,因為問題可能會導致 SyncLock 誤用。 例如,此命令可能會防止競爭狀況,但造成死結。InterLocked
. 允許在基本數值變數上選取一組安全線程作業。
如需詳細資訊,請參閱 線程和線程處理。