對於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