【问题标题】:How to set name to a Win32 Thread?如何将名称设置为 Win32 线程?
【发布时间】:2010-10-28 16:46:15
【问题描述】:

如何为 Win32 线程设置名称。我没有找到任何 Win32 API 来实现相同的功能。基本上我想在日志文件中添加线程名称。 TLS(线程本地存储)是唯一的方法吗?

【问题讨论】:

标签: windows multithreading winapi


【解决方案1】:

如果您的应用程序在 Windows 1607+ 版本上运行,您可以使用 SetThreadDescription()

【讨论】:

【解决方案2】:

根据与 Microsoft 调试团队负责人的讨论(有关详细信息,请参阅下面的链接),SetThreadDescription API 是 Microsoft 将使用的 API,以支持在本机代码中正式命名线程。 “正式”是指 MS 支持的用于命名线程的 API,而不是当前仅在 Visual Studio 中运行进程时才有效的当前异常抛出 hack。

此 API 从 Windows 10 版本 1607 开始可用。

然而,目前几乎没有工具支持,因此您设置的名称在 Visual Studio 或 WinDbg 调试器中不可见。但是,截至 2017 年 4 月,Microsoft xperf/WPA 工具确实支持它(通过此 API 命名的线程的名称将在这些工具中正确显示)。

如果您希望看到它获得更好的支持,例如在 WinDbg、Visual Studio 和故障转储文件中,请使用此链接为它投票:

https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/17608120-properly-support-native-thread-naming-via-the-sett

【讨论】:

  • 更新:Windows 性能分析器和即将推出的 WinDbg 版本现在支持显示通过 SetThreadDescriptionAPI 设置的线程名称。 blogs.msdn.microsoft.com/windbg/2017/06/29/…
  • 更新:从 15.6 版开始,Visual Studio 2017 现在能够在转储调试中显示通过 SetThreadDescription API 设置的线程名称。此功能需要在 Windows 10 Fall Creators Update 或更高版本上收集转储。 docs.microsoft.com/en-us/visualstudio/releasenotes/…
  • 注意:虽然 VS 发行说明声明 SetThreadDescription() 允许您在 Visual Studio 15.6+ 的转储中查看线程名称,但我在 VS 15.9.2 中确认它还允许您查看正常调试时的线程名称。我正在与文档团队一起澄清这一点 (github.com/MicrosoftDocs/visualstudio-docs/issues/2057)
【解决方案3】:

您可以使用线程本地存储对象来存储名称。例如,

__declspec( thread ) char threadName[32];

然后您可以从线程中写入和读取它。这在记录器应用程序中可能很有用,您希望为每条消息打印出线程的名称。您可能希望在线程启动后立即写入此变量,同时抛出 Microsoft 异常 (https://stackoverflow.com/a/10364541/364818),以便调试器也知道线程名称。

【讨论】:

    【解决方案4】:

    另一种方法是在线程的 TEB 的 ArbitraryUserPointer 字段中存储指向名称的指针。这可以在运行时写入和读取。

    有一篇名为 "Debugging With The Thread Information Block" 的 CodeProject 文章向您展示了如何执行此操作。

    【讨论】:

      【解决方案5】:

      Win32 线程没有名称。有一个 Microsoft 约定,应用程序会引发包含线程名称的特殊 SEH 异常。这些异常可以被调试器拦截并用于指示线程名称。有几个答案涵盖了这一点。

      但是,这一切都由调试器处理。线程本身是无名对象。因此,如果您想将名称与线程相关联,则必须开发自己的机制。虽然您可以使用只允许您从该线程中执行的代码中获取名称的线程本地存储。因此,线程 ID 和名称之间的全局映射似乎是最自然和最有用的方法。

      【讨论】:

        【解决方案6】:

        http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.90).aspx

        //
        // Usage: SetThreadName (-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, char* threadName)
        {
           THREADNAME_INFO info;
           info.dwType = 0x1000;
           info.szName = threadName;
           info.dwThreadID = dwThreadID;
           info.dwFlags = 0;
        
           __try
           {
              RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
          }
          __except(EXCEPTION_EXECUTE_HANDLER)
          {
          }
        }
        

        【讨论】:

        • 这行不通,除非您在调试器下运行代码。它当然不能用于在实时系统中记录线程的名称。
        【解决方案7】:

        您始终可以将这些信息存储在合适的数据结构中。使用散列或映射将 GetThreadId() 映射到此名称。由于 GetThreadId() 始终是唯一标识符,因此可以正常工作。

        干杯!

        当然,如果他创造了很多 线程,该哈希图将慢慢填充 起来并使用越来越多的内存,所以 一些清理程序可能是 好东西。

        你完全正确。当一个线程死亡时,它在映射中的对应条目自然应该被删除。

        【讨论】:

        • 当然,如果他创建了很多线程,那个hashmap会慢慢填满,使用越来越多的内存,所以一些清理过程可能也是一件好事。
        • downvoting 因为我也需要这个问题的答案,但出于分析原因,我实际上需要为线程设置一个名称。您的回答没有回答所提出的问题:P
        【解决方案8】:

        如果您想在调试器(windbg 或 Visual Studio)中查看线程的名称: http://blogs.msdn.com/stevejs/archive/2005/12/19/505815.aspx

        我实际上不确定是否有反向方法来获取线程名称。但 TLS 听起来像是要走的路。

        【讨论】:

        • 你有错误的印象。线程的名称与线程一起存储。它仅存储在调试器中,因此仅在附加调试器时才存在。如果没有调试器,异常只会运行到下面的异常过滤器中,就像什么都没发生一样继续。当应用程序不在调试器下运行时,线程名称会丢失。
        【解决方案9】:

        这有帮助吗? How to: Set a Thread Name in Native Code

        在托管代码中,只需设置相应 Thread 对象的 Name 属性即可。

        【讨论】:

        • 我猜它只是让 Visual Studio 调试器显示线程名称。
        • 其他调试器也纷纷效仿,响应相同的信号
        • 正如thetweaker.wordpress.com/2009/04/10/naming-threads 所说,它们都是通过将线程名称存储在调试器数据结构中来实现的。因此,如果没有调试器,则不会存储名称,也无法用于记录目的。也有讨厌的错误:当你尝试调试它时,它突然又可以工作了。
        猜你喜欢
        • 1970-01-01
        • 2012-03-11
        • 1970-01-01
        • 2017-12-25
        • 2012-07-27
        • 1970-01-01
        • 1970-01-01
        • 2019-12-25
        • 1970-01-01
        相关资源
        最近更新 更多