调试线程的提示
本文提供了调试线程的有用信息,包括有关为本机代码和托管代码设置线程名称的信息。
C/C++ 提示
下面是在调试本机代码中的线程时可以使用的一些提示:
可以通过在“监视”窗口或“快速监视”对话框中键入
@TIB
来查看“线程信息块”的内容 。可以通过在“监视”窗口或“快速监视”对话框中输入
@Err
来查看当前线程的上一个错误代码 。可以使用 C 运行库 (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 上创建时,线程名称才可见。
示例:
#include <windows.h>
#include <processthreadsapi.h>
int main()
{
HRESULT r;
r = SetThreadDescription(
GetCurrentThread(),
L"ThisIsMyThreadName!"
);
return 0;
}
通过引发异常来设置线程名称
在程序中设置线程名称的另一种方法是,通过引发专门配置的异常,将所需的线程名称传达给 Visual Studio 调试程序。
优点:
- 适用于所有版本的 Visual Studio。
注意:
- 仅在使用基于异常的方法时附加了调试器的情况下才起作用。
- 使用此方法设置的线程名称在转储或性能分析工具将不可用。
示例:
下面显示的 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 属性。
示例
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();
}