偵錯執行緒的提示
本文提供偵錯執行緒的實用資訊,包括設定原生和受控程式碼執行緒名稱的相關資訊。
C/C++ 提示
在機器碼中對執行緒執行偵錯時,有下列幾個訣竅:
您可以在 [監看式] 視窗或 [快速監看式] 對話方塊中鍵入
@TIB
,以檢視執行緒資訊區塊的內容。您可以在 [監看式] 視窗或 [快速監看式] 對話方塊中輸入
@Err
,以檢視目前執行緒的最後錯誤碼。C 執行階段程式庫 (CRT) 功能可能有助於執行多執行緒應用程式偵錯。 如需詳細資訊,請參閱 _malloc_dbg。
用 C/C++ 設定執行緒名稱
在所有 Visual Studio 版本中,都可以將執行緒命名。 在偵錯執行中處理序時,執行緒命名對於在 [執行緒] 視窗中識別感興趣的執行緒非常有用。 透過損毀傾印檢查執行事後剖析偵錯,以及使用各種工具分析效能擷取時,具有可辨識名稱的執行緒也很有用。
設定執行緒名稱的方式
設定執行緒名稱的方式有兩種。 第一種方式是透過 SetThreadDescription 函式。 第二種方式是在 Visual Studio 偵錯工具附加到處理序時擲回特定的例外狀況。 每種方式都有其優點和注意事項。 從 Windows 10 版本 1607 或 Windows Server 2016 開始,支援使用 SetThreadDescription
。
值得注意的是,如果需要,這兩種方法可以一起使用,因為它們的運作機制彼此獨立。
使用 SetThreadDescription
來設定執行緒名稱
優點:
- 無論叫用 SetThreadDescription 時偵錯工具是否已附加到處理序,在 Visual Studio 中進行偵錯時,都可以看到執行緒名稱。
- 透過在 Visual Studio 中載入損毀傾印來執行事後剖析偵錯時,可以看到執行緒名稱。
- 使用其他工具 (例如 WinDbg 偵錯工具和 Windows Performance Analyzer 效能分析器) 時,也可以看到執行緒名稱。
警告:
- 只有在 Visual Studio 2017 15.6 版和更新版本中,才可以看到執行緒名稱。
- 事後剖析偵錯損毀傾印檔案時,只有損毀是在 Windows 10 版本1607、Windows Server 2016 或更新版本的 Windows 上建立時,才可以看到執行緒名稱。
範例:
#include <windows.h>
#include <processthreadsapi.h>
int main()
{
HRESULT r;
r = SetThreadDescription(
GetCurrentThread(),
L"ThisIsMyThreadName!"
);
return 0;
}
透過擲回例外狀況來設定執行緒名稱
在程式中設定執行緒名稱的另一種方式是:透過擲回特別設定的例外狀況,將所需的執行緒名稱傳達給 Visual Studio 偵錯工具。
優點:
- 適用於所有版本的 Visual Studio。
警告:
- 只有在使用例外狀況的方法時已附加偵錯工具才適用。
- 使用此方法設定的執行緒名稱,無法用於傾印或效能分析工具。
範例:
下面顯示的 SetThreadName
函式示範這種例外狀況的方法。 請注意,執行緒名稱會自動複製到執行緒中,因此,在SetThreadName
呼叫完成之後即可釋放threadName
參數的記憶體。
//
// Usage: SetThreadName ((DWORD)-1, "MainThread");
//
#include <windows.h>
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetThreadName(DWORD dwThreadID, const char* threadName) {
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
__try{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER){
}
#pragma warning(pop)
}
在受控碼中設定執行緒名稱
在所有 Visual Studio 版本中,都可以將執行緒命名。 將執行緒命名後,在 [執行緒] 視窗中追蹤執行緒會很方便。
若要在 Managed 程式碼內設定執行緒名稱,請使用 Name 屬性。
範例
public class Needle
{
// This method will be called when the thread is started.
public void Baz()
{
Console.WriteLine("Needle Baz is running on another thread");
}
}
public void Main()
{
Console.WriteLine("Thread Simple Sample");
Needle oNeedle = new Needle();
// Create a Thread object.
System.Threading.Thread oThread = new System.Threading.Thread(oNeedle.Baz);
// Set the Thread name to "MyThread".
oThread.Name = "MyThread";
// Starting the thread invokes the ThreadStart delegate
oThread.Start();
}