1. 程式人生 > >對於linux中執行緒id的討論

對於linux中執行緒id的討論

在LINUX系統中,POSIX threads庫提供了pthread_t來標識一個執行緒,通過pthread_self()可以得到,如下:

#include <iostream>
#include <pthread.h>

using namespace std;

void* thread_func(void*)
{
    //pthread_t other_thread_id = pthread_self();
    //cout << "other_thread_id=" << other_thread_id << endl;
return NULL; } int main(int argc, char *argv[]) { pthread_t t1, t2; pthread_create(&t1, NULL, thread_func, NULL); cout << t1 << endl; pthread_join(t1, NULL); //pthread_create(&t2, NULL, thread_func, NULL); //cout << t2 << endl; //pthread_join(t2, NULL);
return 0; }

得到結果:
這裡寫圖片描述
typedef unsigned long int pthread_t;
這個數值很大。而且比較兩個執行緒是否相同也需要用pthread_equal(pthread_t t1, pthread_t t2)來比較。

那麼,使用pthread_t來標識執行緒id是否是最合適的?
答案是否
原因:這個pthread_t的值很大,無法作為一些容器的key值。
pthread_t是由POSIX pthread庫內部提供的,只在程序內部有意義,無法關聯作業系統的任務排程之類的資訊。比方說在/proc查詢不到關於pthread_t得到的task。
glibc的Pthreads實現實際上把pthread_t作為一個結構體指標,指向一塊動態分配的記憶體,但是這塊記憶體是可以反覆使用的,也就是說很容易造成pthread_t的重複。也就是說pthreads只能保證同一程序內,同一時刻的各個執行緒不同;不能保證同一個程序全程時段每個執行緒具有不同的id,不能保證執行緒id的唯一性。下面可以通過一個例子:

#include <iostream>
#include <pthread.h>

using namespace std;

void* thread_func(void*)
{
    //pthread_t other_thread_id = pthread_self();
    //cout << "other_thread_id=" << other_thread_id << endl;
    return NULL;
}


int main(int argc, char *argv[])
{
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread_func, NULL);
    cout << t1 << endl;
    pthread_join(t1, NULL);

    pthread_create(&t2, NULL, thread_func, NULL);
    cout << t2 << endl;
    pthread_join(t2, NULL);
    return 0;
}

執行結果:
這裡寫圖片描述

上述原因顯然表明pthread_t是不適合作為執行緒的標識的。所以應該使用什麼來標識呢。
首先對於程序id,有函式getpid返回的pid_t來標識。那麼執行緒有沒有類似的gettid來標識呢。
在LINUX系統中,建議使用gettid系統呼叫的返回值作為執行緒id,這麼做的原因:
返回值是一個pid_t,其值是一個很小的整數,方便輸出。
在linux系統中,它直接標識核心任務排程id,可通過/proc檔案系統中找到對應項:/proc/tid 或者 /proc/pid/task/tid,方便定位到具體執行緒
任何時刻都是唯一的,並且由於linux分配新的pid採用遞增輪迴辦法,短時間內啟動多個執行緒也會具有不同的id
0是非法值,作業系統第一個程序init的pid是1

glibc沒有封裝這個gettid,需要我們手動封裝。
舉例:

#include <iostream>
#include <pthread.h>
#include <sys/syscall.h>//獲取執行緒id的系統呼叫標頭檔案
#include <sys/types.h>
#include <unistd.h>

using namespace std;

void* thread_func(void*)
{
    //獲取執行緒id的系統呼叫
    cout << syscall(__NR_gettid) << endl;
    return NULL;
}


int main(int argc, char *argv[])
{
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread_func, NULL);
    cout << t1 << endl;
    pthread_join(t1, NULL);

    pthread_create(&t2, NULL, thread_func, NULL);
    cout << t2 << endl;
    pthread_join(t2, NULL);
    return 0;
}

結果:
這裡寫圖片描述
和pthread_t的描述完全不一樣。

使用比較優雅的方式是定義一個gettid的巨集

#include <sys/syscall.h>//獲取執行緒id的系統呼叫標頭檔案
#include <sys/types.h>
#include <unistd.h>

//定義巨集
#define gettid() syscall(__NR_gettid)

驗證TID是否正確的方法:
檢視程序pid
(1) ps ux | grep prog_name
(2) pgrep prog_name
檢視執行緒tid
(1) ps -efL | grep prog_name
(2) ls /proc/pid/task