開始對多執行緒應用程式進行偵錯 (C#、Visual Basic、C++)
Visual Studio 提供數個工具和使用者介面元素,可協助您偵錯多執行緒應用程式。 本教學課程示範如何使用執行緒標記、[平行堆疊] 視窗、[平行監看式] 視窗、條件中斷點和篩選中斷點。 完成本教學課程會讓您熟悉 Visual Studio 功能,以偵錯多執行緒應用程式。
這兩篇文章提供有關使用其他多執行緒偵錯工具的其他資訊:
若要使用 [偵錯位置] 工具列和 [執行緒] 視窗,請參閱逐步解說:偵錯多執行緒應用程式。
如需使用 Task (受控程式碼) 和並行執行階段 (C++) 的範例,請參閱逐步解說:偵錯平行應用程式。 如需適用於大部分多執行緒應用程式類型的一般偵錯秘訣,請閱讀該文章和本主題。
第一個步驟是建立多執行緒應用程式專案。
建立多執行緒應用程式專案
開啟 Visual Studio 並建立新專案。
如果開始視窗未開啟,請選擇 [檔案]>[開始視窗]。
在開始視窗中,選擇 [建立新專案]。
在 [建立新專案] 視窗的搜尋方塊中輸入或鍵入 ASP.NET。 接下來,從語言清單中選擇 C#、C++ 或 Visual Basic,然後從平台清單中選擇 Windows。
在您套用語言和平台的篩選條件之後,請選擇 .NET 或 C++ 的 [主控台應用程式] 範本,然後選擇 [下一步]。
注意
如果看不到正確的範本,請移至 [工具]>[取得工具和功能...],這將開啟 Visual Studio 安裝程式。 選擇 [NET 桌面開發] 或 [使用 C++ 的桌面開發] 工作負載,然後選擇 [修改] 按鈕。
在設定新專案視窗中,於專案名稱方塊中鍵入或輸入 MyThreadWalkthroughApp。 然後,選擇 [下一步] 或 [建立],無論哪個選項可用。
對於 .NET Core 或 .NET 5+ 專案,請選擇建議的目標框架或 .NET 8,然後選擇 [建立]。
新的主控台專案隨即出現。 建立專案之後,便會出現來源檔案。 根據您所選擇的語言,原始程式檔的名稱可能是 Program.cs、MyThreadWalkthroughApp.cpp 或 Module1.vb。
刪除出現在原始程式檔中的程式碼,並將其取代為下列更新的程式碼。 為程式碼設定選擇適當的程式碼片段。
using System; using System.Threading; public class ServerClass { static int count = 0; // The method that will be called when the thread is started. public void InstanceMethod() { Console.WriteLine( "ServerClass.InstanceMethod is running on another thread."); int data = count++; // Pause for a moment to provide a delay to make // threads more apparent. Thread.Sleep(3000); Console.WriteLine( "The instance method called by the worker thread has ended. " + data); } } public class Simple { public static void Main() { for (int i = 0; i < 10; i++) { CreateThreads(); } } public static void CreateThreads() { ServerClass serverObject = new ServerClass(); Thread InstanceCaller = new Thread(new ThreadStart(serverObject.InstanceMethod)); // Start the thread. InstanceCaller.Start(); Console.WriteLine("The Main() thread calls this after " + "starting the new InstanceCaller thread."); } }
在 [File] \(檔案\) 功能表上,選取 [Save All] \(全部儲存\)。
(僅限 Visual Basic) 在 [方案總管] (右窗格) 中,以滑鼠右鍵按一下專案節點,然後選擇 [屬性]。 在 [應用程式] 索引標籤下,將 Startup 物件變更為 Simple。
偵錯多執行緒應用程式
在原始程式碼編輯器中,尋找下列程式碼片段:
在
Thread.Sleep
的左裝訂邊,或是 (針對 C++)std::this_thread::sleep_for
陳述式按一下滑鼠左鍵以插入新的中斷點。在裝訂邊中,紅色圓圈表示這個位置已設定中斷點。
在 [偵錯] 功能表中,選取 [開始偵錯] (F5)。
Visual Studio 會建置解決方案,應用程式會以附加的偵錯工具開始執行,然後應用程式會在中斷點停止。
在原始程式碼編輯器中,找出包含中斷點的行。
尋找執行緒標記
在 [偵錯工具列] 中,選取 [在來源中顯示執行緒] 按鈕 。
按 F11 兩次,將偵錯工具前移。
查看來源視窗左邊的裝訂邊。 在此行中,請留意類似兩個扭曲執行緒的執行緒標記圖示 。 執行緒標記表示執行緒會停在這個位置上。
中斷點可以部分隱藏執行緒標記。
將指標移到執行緒標記上。 此時會出現一個 DataTip,告訴您每個已停止執行緒的名稱和執行緒識別碼。 在此情況下,名稱可能是
<noname>
。選取執行緒標記以查看捷徑功能表上的可用選項。
檢視執行緒位置
在 [平行堆疊] 視窗中,您可以在 [執行緒] 檢視和 (針對工作型程式設計) [工作] 檢視之間切換,且您可以檢視每個執行緒的呼叫堆疊資訊。 在此應用程式中,我們可以使用 [執行緒] 檢視。
選擇偵錯>視窗>平行堆疊來開啟平行堆疊視窗。 您應該會看到類似以下的畫面。 確切資訊會根據每個執行緒、硬體和程式設計語言的目前位置而有所不同。
在此範例中,從左至右,我們會看到受控程式碼的這項資訊:
- 目前執行緒 (黃色箭號) 已輸入
ServerClass.InstanceMethod
。 您可以將滑鼠停留在ServerClass.InstanceMethod
上,以檢視執行緒的執行緒識別碼和堆疊框架。 - 執行緒 31724 正在等候執行緒 20272 所擁有的鎖定。
- 主執行緒 (左側) 已在 [外部程式碼] 停止,如果您選擇 [顯示外部程式碼],您可以詳細檢視此執行緒。
在此範例中,從左至右,我們會看到受控程式碼的這項資訊:
- 主執行緒 (左側) 已在
Thread.Start
停止,其中停止點是由執行緒標記圖示 表示。 - 兩個執行緒已進入
ServerClass.InstanceMethod
,其中一個是目前執行緒 (黃色箭號),而另一個執行緒已在Thread.Sleep
停止。 - 新的執行緒 (右側) 也正在啟動,但在
ThreadHelper.ThreadStart
上停止。
- 目前執行緒 (黃色箭號) 已輸入
若要在清單檢視中檢視執行緒,請選取 [偵錯]>[Windows]>[執行緒]。
在此檢視中,您可以輕鬆地看到執行緒 20272 是主要執行緒,且目前位於外部程式碼中,特別是 System.Console.dll。
注意
如需關於使用 [執行緒] 視窗的詳細資訊,請參閱逐步解說:偵錯多執行緒應用程式。
以滑鼠右鍵按一下 [平行堆疊] 或 [執行緒] 視窗中的項目,以查看捷徑功能表上的可用選項。
您可以透過這些右鍵功能表採取各種動作。 在本教學課程中,您會在 [平行監看式] 視窗 (下一節) 中探索更多詳細資料。
在變數上設定監看式
選取偵錯>視窗>平行監看式>平行監看式 1,以開啟平行監看式視窗。
選取您在其中看到
<Add Watch>
文字的儲存格 (或第四欄的空白標題儲存格),然後輸入data
。每個執行緒的資料變數值隨即會出現在視窗中。
選取您在其中看到
<Add Watch>
文字的儲存格 (或第五欄的空白標題儲存格),然後輸入count
。每個執行緒的
count
變數值隨即會出現在視窗中。 如果您還沒看到太多資訊,請嘗試按 F11 幾次,以在偵錯工具中將執行緒的執行前移。以滑鼠右鍵按一下視窗中的其中一個資料列,以查看可用的選項。
將執行緒加上旗標和取消旗標
您可以標幟執行緒以追蹤重要的執行緒,並忽略其他執行緒。
在 [平行監看式] 視窗中,按住 Shift 鍵並選取多個資料列。
以滑鼠右鍵按一下並選取 [標幟]。
所有選取的執行緒都會加上標幟。 現在,您可以篩選只顯示已標幟的執行緒。
在 [平行監看式] 視窗中,選取 [僅顯示標幟的執行緒] 按鈕 。
清單中只會顯示加上標幟的執行緒。
提示
在標幟某些執行緒之後,您可以在程式碼編輯器中以滑鼠右鍵按一下程式碼,然後選擇 [執行標幟的執行緒至資料指標]。 請務必選擇所有標幟執行緒都會到達的程式碼。 Visual Studio 會在選取的程式碼上暫停執行緒,可凍結及解除凍結執行緒,更輕鬆地控制執行順序。
再次選取 [僅顯示標幟的執行緒] 按鈕,以切換回 [顯示所有執行緒] 模式。
若要取消標幟執行緒,請在 [平行監看式] 視窗中,以滑鼠右鍵按下一或多個標幟的執行緒,然後選取 [取消標幟]。
凍結及解除凍結執行緒執行
提示
您可以凍結及解除凍結 (暫停和繼續) 執行緒,以控制執行緒執行工作的順序。 這可協助您解決並行問題,例如死結和競爭條件。
在 [平行監看式] 視窗中,選取所有資料列時,以滑鼠右鍵按一下並選取 [凍結]。
在第二個資料行中,每個資料列都會顯示暫停圖示。 暫停圖示表示執行緒已凍結。
只選取一個資料列,以取消選取所有其他資料列。
以滑鼠右鍵按一下資料列,然後選取 [解除凍結]。
暫停圖示會消失在這個資料列上,表示執行緒不再凍結。
切換至程式碼編輯器,然後按 F11。 只會執行未凍結的執行緒。
應用程式也可以具現化一些新的執行緒。 任何新的執行緒都已取消標幟,且不會凍結。
遵循具有條件中斷點的單一執行緒
在偵錯工具中遵循單一執行緒的執行會很有用。 其中一種方法是凍結您不需要的執行緒。 在某些情況下,您可能需要追蹤單一執行緒,而不凍結其他執行緒,例如重現特定錯誤。 若要追蹤執行緒而不凍結其他執行緒,您必須避免在需要的執行緒上中斷程式碼。 您可以設定有條件的中斷點來執行此工作。
您可以在不同的條件下設定中斷點,例如執行緒名稱或執行緒識別碼。 對於每個執行緒而言,設定您所知資料的條件可能很有用。 當您對某些特定資料值比任何特定執行緒更感興趣時,此方法在偵錯期間很常見。
以滑鼠右鍵按一下您先前建立的中斷點,並選取 [條件]。
在 [中斷點設定] 視窗中,針對條件運算式輸入
data == 5
。提示
如果您較需要特定執行緒,請針對條件使用執行緒名稱或執行緒識別碼。 若要在 [中斷點設定] 視窗中執行這項操作,請選取 [篩選] 而不是 [條件運算式],然後遵循篩選提示。 您可能需要在應用程式程式碼中為執行緒命名,因為當您重新啟動偵錯工具時,執行緒 ID 會變更。
關閉 [中斷點設定] 視窗。
選取 [重新啟動] 按鈕,以重新啟動偵錯工作階段。
您會在資料變數值為 5 的執行緒上中斷程式碼。 在 [平行監看式] 視窗中,尋找指出目前偵錯工具內容的黃色箭號。
現在,您可以逐步執行 (step over) 程式碼 (F10) 並逐步執行 (step into) 程式碼 (F11),然後遵循單一執行緒的執行。
只要中斷點條件對執行緒而言是唯一的,且偵錯工具不會在其他執行緒上叫用任何其他中斷點 (您可能需要加以停用),您就可以逐步執行 (step over) 程式碼並逐步執行 (step into) 程式碼,而不切換至其他執行緒。
注意
當您將偵錯工具前移時,所有執行緒都會執行。 不過,除非其中一個其他執行緒叫用中斷點,否則偵錯工具不會在其他執行緒上中斷程式碼。