Share via


偵錯執行緒的提示

本文提供偵錯執行緒的實用資訊,包括設定原生和受控程式碼執行緒名稱的相關資訊。

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();
}