1. 程式人生 > >Linux/Windows下C++設定執行緒名字方便多執行緒除錯

Linux/Windows下C++設定執行緒名字方便多執行緒除錯

C++多執行緒程式設計,除錯是一個大問題,原因之一就是,執行緒名字繼承了父程序的名字,因此導致同一段程式碼的不同執行緒名字一樣;而且還會導致執行緒的名字怪怪的,不好看。

因此,如果在程式碼中可以設定執行緒的名字就好了,這樣在除錯中就可以看到期望的執行緒名字,這樣便於除錯。

由於編寫跨平臺程式碼,因此,執行緒設定名字對Windows和Linux來說都很重要,這裡分別來講下。

Windows

在Visual Studio除錯的執行緒選項卡看到設定名字後的執行緒的名字:
在這裡插入圖片描述

可以看到,我把其中一條執行緒的名字改成了FlushHip-TEST-THREAD,要是沒有設定,會是怎麼樣的呢,看下圖:
在這裡插入圖片描述


看到了吧,這樣的名字只有老天知道是哪條執行緒。

那麼在Windows中如何用程式碼來設定執行緒名字呢?

#include <windows.h>

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;

void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName)
{
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = szThreadName;
    info.dwThreadID = dwThreadID;
    info.dwFlags = 0;

    __try
    {
        RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
    }
    __except(EXCEPTION_CONTINUE_EXECUTION)
    {
    }
}

利用結構化異常SHE和RaiseException還有異常程式碼0x406D1388,可以實現設定執行緒名字。

The native method of setting the thread name is implemented by raising an SEH exception that is continued. If you go to the docs on RaiseException you’ll see part of the reason for this strange mechanism. An attached native debugger will get a ‘first chance’ notification of the exception. Raising an exception is precisely what you need to do to get the native debugger’s attention. The one raised here (0x406D1388) is recognized by VS (and WinDbg).

SHE是編譯期相關的,這樣子設定執行緒名字的方式只能用於VS。

如何使用這個函式呢?首先,在本執行緒中設定名字只需要把dwThreadID設定成-1就行了,設定別的執行緒的名字可以指定別的執行緒的ID。

std::thread th([]()
{
    SetThreadName(-1, "FlushHip-TEST-THREAD");
    std::this_thread::sleep_for(std::chrono::seconds(1000));
});

Linux

Linux下可以使用prctlpthread_setname_np來設定執行緒名字,注意,這個設定執行緒名字是真的會設定執行緒名字。在Linux下我們可以通過下面兩種方式來看看執行緒的名字

  • cat /proc/pid/task/[tid]/comm
  • ps ps -eL -o lwp,comm | grep tid

也來看看設定前和設定後的效果:

在這裡插入圖片描述

在這裡插入圖片描述

可以看到設定前,主執行緒和輔助執行緒都是同一名字,無法分辨,但是,設定後,可以很明顯看到差別,且,我設定的執行緒名字是FlushHip-TEST-THREAD,但是顯示出來的只有FlushHip-TEST-T,這是因為Linux下利用prctlpthread_setname_np來設定執行緒名字,而這兩個函式的man手冊中明確指出了,執行緒名字最多支援16個位元組的空間,FlushHip-TEST-T是15個字元,還有結尾的\0,剛好16個字元。

長度限制也是Linux設定執行緒名字的一個短板,因此,在設定執行緒名字的時候儘量精簡一些。看看程式碼實現:

#include <sys/prctl.h>
#include <pthread.h>

void SetThreadName(pthread_t thread, const char *name)
{
    if (thread == -1)
        prctl(PR_SET_NAME, name)
    else
        pthread_setname_np(thread, name);
}

我們可以在GDB下打上斷點,用info threads看看實際效果:

在這裡插入圖片描述

這樣子除錯多執行緒的時候就方便很多了。


參考: