【问题标题】:std::thread - naming your threadstd::thread - 命名你的线程
【发布时间】:2012-04-24 16:19:24
【问题描述】:

新的 C++ 有这个 std::thread 类型。奇迹般有效。 现在我想给每个线程一个名称以便于调试(就像 java 允许的那样)。 使用 pthreads 我会这样做:

pthread_setname_np(pthread_self(), "thread_name");

但是我怎样才能用 c++0x 做到这一点呢? 我知道它在 Linux 系统下使用 pthreads,但我想让我的应用程序可移植。有可能吗?

【问题讨论】:

  • 在 Windows 上,线程名称是调试器属性(即在应用程序本身之外跟踪)。因此,您没有 pthread_getname_np 的等价物
  • 从Windows 10、1607开始,有SetThreadDescription

标签: c++ multithreading c++11


【解决方案1】:

一种可移植的方法是维护一个名称映射,该映射由线程 ID 键入,从thread::get_id() 获得。或者,按照 cmets 中的建议,如果您只需要从线程内访问名称,则可以使用 thread_local 变量。

如果您不需要可移植性,那么您可以从 thread::native_handle() 获取底层的 pthread_t 并使用它做任何您喜欢的特定于平台的恶作剧。请注意,线程命名函数上的 _np 表示“非 posix”,因此不能保证它们在所有 pthreads 实现中都可用。

【讨论】:

  • “名称映射,以线程 ID 为键” - 还是线程本地存储?当然,假设您正在执行的调试仅来自您想知道其名称的线程内部。
  • 就包装这个想法的设计而言,您可能需要考虑在您的应用程序中使用 ThreadFactory,其目标是在创建时注册线程和/或抽象出所有 #ifdef如果您想使用预编译器来选择特定于平台的代码,则需要 s。
  • 线程命名的全部意义在于使调试更容易,因为调试器会显示线程的名称,所以在内部维护一些名称映射是毫无意义的......
  • @VáclavSlavík:确实,如果您将“更容易调试”解释为“在调试器中显示线程名称”,那么可移植方法将无济于事。不过,它可能对其他调试和线程管理方法很有用。
  • ⁺¹ 详细说明 _np 后缀。
【解决方案2】:

尝试制作一个包装器来处理许多 Linux 和 Windows。请根据需要进行编辑。

#ifdef _WIN32
#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(uint32_t dwThreadID, const char* threadName)
{

  // DWORD dwThreadID = ::GetThreadId( static_cast<HANDLE>( t.native_handle() ) );

   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)
   {
   }
}
void SetThreadName( const char* threadName)
{
    SetThreadName(GetCurrentThreadId(),threadName);
}

void SetThreadName( std::thread* thread, const char* threadName)
{
    DWORD threadId = ::GetThreadId( static_cast<HANDLE>( thread->native_handle() ) );
    SetThreadName(threadId,threadName);
}

#elif defined(__linux__)
#include <sys/prctl.h>
void SetThreadName( const char* threadName)
{
  prctl(PR_SET_NAME,threadName,0,0,0);
}

#else
void SetThreadName(std::thread* thread, const char* threadName)
{
   auto handle = thread->native_handle();
   pthread_setname_np(handle,threadName);
}
#endif

【讨论】:

  • 只是为了澄清Windows部分来自msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
  • 在 Windows 上,您可能还想使用 SetThreadDescription() API:stackoverflow.com/a/41446477/434413。这是新的官方 API,正在更新的 MS 工具中使用(此答案中显示的方法是旧的方法,仅适用于在抛出异常时在 Visual Studio 调试器中运行的进程)。
  • 注意:带有 RaiseException 的 Windows 代码会被调试器实时捕获。 IE。如果您在线程启动时引发一次异常并稍后附加调试器,它将不知道线程名称。
【解决方案3】:

您可以使用std::thread::native_handle 来获取底层实现定义的线程。本机没有标准功能。

你可以找到一个例子here

【讨论】:

    【解决方案4】:

    对于windows [调试器],你可以很容易地使用“正常”的方法; http://msdn.microsoft.com/en-gb/library/xcb2z8hs.aspx

    只需要你可以通过获取的线程id

    #include <windows.h>
    DWORD ThreadId = ::GetThreadId( static_cast<HANDLE>( mThread.native_handle() ) );
    

    【讨论】:

    • 这可能会起作用,但它不是通用的 C++,因此不是跨平台解决方案。无论如何,感谢您的建议。
    • 哦,这肯定只是 windows,但实际上没有人告诉你如何在 windows 上设置它。 (它有效,我用它:)
    【解决方案5】:

    我已经在早于 c++11 的系统中看到了这一点(我们基本上发明了我们自己的 Thread 类,它与 std::thread 非常相似)和我最近写的一个。

    基本上,池实际上将 std::thread 向下放置了 2 层——你有一个 PoolThread 类,它包含一个 std::thread 以及它的名称、ID 等元数据以及将它链接到它的控制的控制结构池和线程池本身。您想在大多数线程代码中使用线程池有几个原因:
    1) 您可以对用户隐藏所有显式的“分离”、“加入”、std::thread 构造上的启动线程等。这会产生更安全和更清洁的代码。
    2) 更好的资源管理:线程过多会比线程过少更严重地削弱性能。构建良好的池可以执行高级操作,例如自动负载平衡和清理挂起或死锁的线程。
    3) 线程重用:std::thread 本身最容易通过在自己的线程上运行每个并行任务来使用。但是线程创建和销毁很昂贵,如果你不小心的话,很容易淹没并行处理的速度提升。因此,让池线程从队列中提取工作任务并仅在接收到某些信号后退出通常更有意义。
    4) 错误处理:std::thread 只是一个执行上下文。如果您正在运行的任务引发未处理的异常或 std::thread ITSELF 失败,则该进程将在那里崩溃。要进行容错多线程,您需要一个 Pool 或类似的东西,它可以快速捕获此类事情并至少在进程终止之前发出有意义的错误消息。

    【讨论】:

    • 关于您的第 2 点:如果您在程序中添加逻辑以清理挂起或死锁的线程,那么您只是试图隐藏错误。我建议改为修复错误。第 4 点也有类似的情况:如果线程顶层出现未处理的异常,则代码中存在致命错误。同样,首要任务应该是修复错误,而不是“优雅地”处理这种情况。
    【解决方案6】:

    在头文件中做:

    const std::string & ThreadName(const std::string name="");
    

    在 src 文件中:

    const std::string & ThreadName(const std::string name)
    {
        static std::atomic_int threadCount{0};
        const thread_local std::string _name = name + std::to_string(threadCount.fetch_add(1));
        return _name; 
    }
    

    用法:

    void myThread()
    {
       ThreadName("myThread"); // Call once at very beginning of your thread creation
       ...
       std::cout << ThreadName() << std::endl; // Anyplace in your code
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多