调试线程的提示

本文提供有关调试线程的有用信息,包括有关设置本机代码和托管代码的线程名称的信息。

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

在托管代码中设置线程名称

线程命名在任何版本的 Visual Studio 中都是可能的。 线程命名可用于跟踪 “线程” 窗口中的线程。

若要在托管代码中设置线程名称,请使用该 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();
}