3、Linux多線程,線程同步
5)線程私人資料
進程內的所有線程共用進程的資料空間,因此全域變數為所有線程所共有。但有時線程也需要儲存自己的私人資料,這時可以建立線程私人資料(Thread-specific Date)TSD來解決。線上程內部,私人資料可以被各個函數訪問,但對其他線程是屏蔽的。例如我們常見的變數errno,它返回標準的出錯資訊。它顯然不能是一個局部變數,幾乎每個函數都應該可以調用它;但它又不能是一個全域變數,否則在A線程裡輸出的很可能是B線程的出錯資訊。要實現諸如此類的變數,我們就必須使用線程資料。我們為每個線程資料建立一個鍵,它和這個鍵相關聯,在各個線程裡,都使用這個鍵來指代線程資料,但在不同的線程裡,這個鍵代表的資料是不同的,在同一個線程裡,它代表同樣的資料內容。
線程私人資料採用了一鍵多值的技術,即一個鍵對應多個數值,訪問資料時好像是對同一個變數進行訪問,但其實是在訪問不同的資料。
建立私人資料的函數有4個:pthread_key_create(建立), pthread_setspecific(設定), pthread_getspecific(擷取), pthread_key_delete(刪除)。
#include <pthread.h>
int pthread_key_creadte(pthread_key_t *key,void (*destr_fuction) (void *));
int pthread_setspecific(pthread_key_t key,const void * pointer));
void * pthread_getspecific(pthread_key_t key);
int pthread_key_delete(ptherad_key_t key);
6)線程同步
線程的最大特點是資源的共用性,但資源共用中的同步問題是多線程編程的痛點。linux下提供了多種方式來處理線程同步,最常用的是互斥鎖、條件變數和非同步訊號。
1)互斥鎖(mutex)
通過鎖機制實現線程間的同步。同一時刻只允許一個線程執行一個關鍵區段的代碼。
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_destroy(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *
(1)先初始化鎖init()或靜態賦值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER
attr_t有:
PTHREAD_MUTEX_TIMED_NP:其餘線程等待隊列
PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖,允許線程多次加鎖,不同線程,解鎖後重新競爭
PTHREAD_MUTEX_ERRORCHECK_NP:檢錯,與一同,線程請求已用鎖,返回EDEADLK;
PTHREAD_MUTEX_ADAPTIVE_NP:適應鎖,解鎖後重新競爭
(2)加鎖,lock,trylock,lock阻塞等待鎖,trylock立即返回EBUSY
(3)解鎖,unlock需滿足是加鎖狀態,且由加鎖線程解鎖
(4)清除鎖,destroy(此時鎖必需unlock,否則返回EBUSY,//Linux下互斥鎖不佔用記憶體資源
範例程式碼
?
#include <cstdio> #include <cstdlib> #include <unistd.h> #include <pthread.h> #include "iostream" using namespace std; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int tmp; void * thread ( void *arg) { cout << "thread id is " << pthread_self() << endl; pthread_mutex_lock(&mutex); tmp = 12; cout << "Now a is " << tmp << endl; pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t id; cout << "main thread id is " << pthread_self() << endl; tmp = 3; cout << "In main func tmp = " << tmp << endl; if (!pthread_create(&id, NULL, thread , NULL)) { cout << "Create thread success!" << endl; } else { cout << "Create thread failed!" << endl; } pthread_join(id, NULL); pthread_mutex_destroy(&mutex); return 0; } |
編譯: g++ -o thread testthread.cpp -lpthread
說明:pthread庫不是Linux系統預設的庫,串連時需要使用靜態庫libpthread.a,所以在使用pthread_create()建立線程,以及調用pthread_atfork()函數建立fork處理常式時,需要連結該庫。在編譯中要加 -lpthread參數。
5)線程私人資料
進程內的所有線程共用進程的資料空間,因此全域變數為所有線程所共有。但有時線程也需要儲存自己的私人資料,這時可以建立線程私人資料(Thread-specific Date)TSD來解決。線上程內部,私人資料可以被各個函數訪問,但對其他線程是屏蔽的。例如我們常見的變數errno,它返回標準的出錯資訊。它顯然不能是一個局部變數,幾乎每個函數都應該可以調用它;但它又不能是一個全域變數,否則在A線程裡輸出的很可能是B線程的出錯資訊。要實現諸如此類的變數,我們就必須使用線程資料。我們為每個線程資料建立一個鍵,它和這個鍵相關聯,在各個線程裡,都使用這個鍵來指代線程資料,但在不同的線程裡,這個鍵代表的資料是不同的,在同一個線程裡,它代表同樣的資料內容。
線程私人資料採用了一鍵多值的技術,即一個鍵對應多個數值,訪問資料時好像是對同一個變數進行訪問,但其實是在訪問不同的資料。
建立私人資料的函數有4個:pthread_key_create(建立), pthread_setspecific(設定), pthread_getspecific(擷取), pthread_key_delete(刪除)。
#include <pthread.h>
int pthread_key_creadte(pthread_key_t *key,void (*destr_fuction) (void *));
int pthread_setspecific(pthread_key_t key,const void * pointer));
void * pthread_getspecific(pthread_key_t key);
int pthread_key_delete(ptherad_key_t key);
6)線程同步
線程的最大特點是資源的共用性,但資源共用中的同步問題是多線程編程的痛點。linux下提供了多種方式來處理線程同步,最常用的是互斥鎖、條件變數和非同步訊號。
1)互斥鎖(mutex)
通過鎖機制實現線程間的同步。同一時刻只允許一個線程執行一個關鍵區段的代碼。
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_destroy(pthread_mutex *mutex);
int pthread_mutex_unlock(pthread_mutex *
(1)先初始化鎖init()或靜態賦值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER
attr_t有:
PTHREAD_MUTEX_TIMED_NP:其餘線程等待隊列
PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖,允許線程多次加鎖,不同線程,解鎖後重新競爭
PTHREAD_MUTEX_ERRORCHECK_NP:檢錯,與一同,線程請求已用鎖,返回EDEADLK;
PTHREAD_MUTEX_ADAPTIVE_NP:適應鎖,解鎖後重新競爭
(2)加鎖,lock,trylock,lock阻塞等待鎖,trylock立即返回EBUSY
(3)解鎖,unlock需滿足是加鎖狀態,且由加鎖線程解鎖
(4)清除鎖,destroy(此時鎖必需unlock,否則返回EBUSY,//Linux下互斥鎖不佔用記憶體資源
範例程式碼
?
#include <cstdio> #include <cstdlib> #include <unistd.h> #include <pthread.h> #include "iostream" using namespace std; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int tmp; void * thread ( void *arg) { cout << "thread id is " << pthread_self() << endl; pthread_mutex_lock(&mutex); tmp = 12; cout << "Now a is " << tmp << endl; pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t id; cout << "main thread id is " << pthread_self() << endl; tmp = 3; cout << "In main func tmp = " << tmp << endl; if (!pthread_create(&id, NULL, thread , NULL)) { cout << "Create thread success!" << endl; } else { cout << "Create thread failed!" << endl; } pthread_join(id, NULL); pthread_mutex_destroy(&mutex); return 0; } |
編譯: g++ -o thread testthread.cpp -lpthread
說明:pthread庫不是Linux系統預設的庫,串連時需要使用靜態庫libpthread.a,所以在使用pthread_create()建立線程,以及調用pthread_atfork()函數建立fork處理常式時,需要連結該庫。在編譯中要加 -lpthread參數。