Linux 獲取線程id


目錄

    Linux中,我們知道getpid(2) 可以獲取調用進程的pid,那么如何獲取一個線程的id呢

    可以用系統調用gettid(2)獲取內核中的線程id ,POSIX線程庫提供的pthread_self(3)方法獲取分配的線程id。C++11 std::thread的get_id()方法,封裝的也是POSIX pthread線程庫的線程id。

    內核中的線程id,與pthread線程庫的線程id有何區別?
    在Linux中,線程本質上是一個進程(實現),也就是說,通過系統調用gettid獲取的線程id跟進程id是一樣的。

    glibc的Pthreads實現,把pthread_self返回值類型pthread_t用作一個結構體指針(類型為unsigned long),指向一個動態分配的內存,而且該內存是反復使用的。這也就是說,pthread_t的值很容易重復。Pthreads只能保證同一個進程內,同一時刻的各個線程id不同;但不能保證同一進程先后多個線程具有不同id,不能保證一台機器上多個進程間的id不同。

    因此,pthread_t不適合作為程序中對線程的標識符。建議用gettid(2)系統調用返回值作為線程id,好處在於:

    • 類型是pid_t,其值是一個小整數(最大值/proc/sys/kernel/pid_max,默認32768),便於log輸出;
    • 表示內核的任務調度id,在/proc文件系統中,可以找到對應項:/proc/tid,或/prod/pid/task/tid;
    • 在其他系統工具中容易定位到具體某個線程,如top(1)命令中,可以按線程列出任務,然后找出CPU使用率最高的線程id,再根據log判斷到底哪個線程在耗費CPU;
    • 任何時刻都是全局唯一的,且由於Linux分配新pid采用遞增輪回辦法,短時間內啟動的多個線程也會具有不同的線程id;
    • 0是非法值,因為操作系統的第一個進程init的pid是1,而gettid采用的線程tid本質上是進程pid,因此不能再為1。

    示例

    // 通過pthread線程庫獲取tid
    void* threadFunc(void* arg)
    {
        pthread_t id = pthread_self(); // 獲取Pthreads線程id
        printf("pthread id=%lx\n", id);
    }
    int main()
    {
        pthread_t th;
        pthread_create(&th, NULL, threadFunc, NULL);
        pthread_join(th, NULL);
        return 0;
    }
    
    // C++11 std::thread通過get_id獲取tid
    void threadFunc()
    {
        cout << th.get_id() << endl; // 獲取Pthreads線程id
    }
    int main()
    {
        std::thread th(threadFunc);
        th.join();
        return 0;
    }
    
    // 通過系統調用gettid獲取線程id
    pid_t gettid()
    {
        return static_cast<pid_t>(::syscall(SYS_gettid));
    }
    

    利用thread local緩存線程id
    由於系統調用會陷入內核,頻繁系統調用可能會影響系統性能,有沒有辦法可以避免這個問題?
    答案是有的。考慮到線程id在線程創建后,並不會隨意改變,因此可以用每個線程自帶的thread local(__thread)變量來緩存其線程id值,初值設為0或負數即可。

    // C++11 利用__thread緩存線程id
    __thread int t_tid = 0;
    
    // 用戶接口:獲取線程id
    inline int tid()
    {
        if (t_tid == 0)
        {
            cacheTid();
        }
        return t_tid;
    }
    
    void cacheTid()
    {
        if (t_tid == 0)
        {
            t_tid = gettid();
        }
    }
    
    // 通過系統調用gettid獲取線程id
    pid_t gettid()
    {
        return static_cast<pid_t>(::syscall(SYS_gettid));
    }
    

    參考
    [1]陳碩. Linux多線程服務端編程:使用muduo C++網絡庫[M]. 電子工業出版社, 2013.


    免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM