共用方式為


除錯線程的技巧

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

C/C++ 提示

以下是在本機程式碼中偵錯執行緒時可以使用的一些提示:

  • 您可以在「監看」視窗或「快速觀察」對話方塊中鍵入@TIB以檢視「執行緒資訊區塊」的內容。

  • 您可以在 [@Err 視窗或 [快速觀察] 對話方塊中輸入,以檢視目前執行緒的最後一個錯誤碼。

  • C Run-Time 程式庫 (CRT) 函式可用於偵錯多執行緒應用程式。 如需詳細資訊,請參閱 _malloc_dbg

在 C/C++ 中設定執行緒名稱

在任何版本的 Visual Studio 中都可以進行執行緒命名。 執行緒命名對於在偵錯執行中進程時識別 [執行緒] 視窗中感興趣的執行緒很有用。 在透過損毀傾印檢查執行事後偵錯,以及使用各種工具分析效能擷取時,具有可辨識的命名執行緒也很有幫助。

設定執行緒名稱的方法

設定執行緒名稱有兩種方法。 第一個是透過 SetThreadDescription 函式。 第二個是在 Visual Studio 偵錯工具附加至程序時拋出特定例外狀況。 每種方法都有優點和注意事項。 從 Windows 10 版本 1607 或 Windows Server 2016 開始支援使用 。SetThreadDescription

值得注意的是,如果需要, 這兩種 方法可以一起使用,因為它們的工作機制彼此獨立。

使用 SetThreadDescription 設定執行緒名稱

優點:

  • 在 Visual Studio 中偵錯時,線程名稱會顯示,不論調試工具在叫用 SetThreadDescription 時是否已附加至進程。
  • 在 Visual Studio 中載入崩潰轉儲進行事後除錯時,執行緒名稱會顯示出來。
  • 使用其他工具時,也會顯示執行緒名稱,例如 WinDbg 偵錯工具和 Windows 效能分析器 效能分析器。

警示:

  • 執行緒名稱只會在 Visual Studio 2017 15.6 版和更新版本中顯示。
  • 在進行死後偵錯崩潰傾印檔案時,僅當崩潰是在 Windows 10 1607 版、Windows Server 2016 或更新版本的 Windows 上建立時,線程名稱才會顯示。

Example:

#include <windows.h>
#include <processthreadsapi.h>

int main()
{
    HRESULT r;
    r = SetThreadDescription(
        GetCurrentThread(),
        L"ThisIsMyThreadName!"
    );

    return 0;
}

透過丟擲例外來設定執行緒名稱

在程式中設定執行緒名稱的另一種方式是透過拋出特別設定的例外狀況,將您期望的執行緒名稱傳達給 Visual Studio 偵錯工具。

優點:

  • 適用於所有版本的 Visual Studio。

警示:

  • 只有在使用例外狀況型方法時附加偵錯工具時才有效。
  • 使用此方法設定的執行緒名稱不會可用於記憶體傾印或效能分析工具。

Example:

下面顯示的 SetThreadName 函數示範了這種基於例外狀況的方法。 注意,執行緒名稱會自動複製到執行緒中,以便在呼叫完成後可以釋放threadName參數的SetThreadName記憶體。

//
// 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)
}

在 Managed 程式碼中設定執行程名稱

在任何版本的 Visual Studio 中都可以進行執行緒命名。 執行緒命名對於追蹤 「執行緒」 視窗中的執行緒很有用。

若要在 Managed 程式碼中設定執行緒名稱,請使用 屬性 Name

Example

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