67

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

pthread_setname_np(pthread_self(), "thread_name");

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

4

6 回答 6

41

一种可移植的方法是维护一个名称映射,以线程 ID 为键,从thread::get_id(). thread_local或者,如评论中所建议的,如果您只需要从线程内访问名称,则可以使用变量。

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

于 2012-04-12T10:19:33.647 回答
29

尝试制作一个包装器来处理许多 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
于 2014-05-27T21:36:49.340 回答
12

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

你可以在这里找到一个例子。

于 2012-04-12T10:16:01.457 回答
5

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

只需要您可以通过以下方式获取的线程ID

#include <windows.h>
DWORD ThreadId = ::GetThreadId( static_cast<HANDLE>( mThread.native_handle() ) );
于 2014-01-16T20:23:29.317 回答
2

我都在早于 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 或类似的东西,它可以快速捕获此类事情并至少在进程终止之前发出有意义的错误消息。

于 2014-05-30T21:16:47.567 回答
0

在头文件中执行:

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
}
于 2021-12-02T14:52:16.900 回答