線程的最大特點是資源的共享性,但資源共享中的同步問題是多線程編程的難點。linux下提供了多種方式來處理線程同步,最常用的是互斥鎖、條件變量和信號量。
1)互斥鎖(mutex)
? ? 通過鎖機制實現線程間的同步。同一時刻只允許一個線程執行一個關鍵部分的代碼。
Cpp代碼 ?
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下互斥鎖不占用內存資源
示例代碼
Cpp代碼 ?
#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參數。
?
2)條件變量(cond)
? ? 利用線程間共享的全局變量進行同步的一種機制。條件變量上的基本操作有:觸發條件(當條件變為 true 時);等待條件,掛起線程直到其他線程觸發條件。
Cpp代碼 ?
int?pthread_cond_init(pthread_cond_t?*cond,pthread_condattr_t?*cond_attr);?????int?pthread_cond_wait(pthread_cond_t?*cond,pthread_mutex_t?*mutex);??int?pthread_cond_timewait(pthread_cond_t?*cond,pthread_mutex?*mutex,const?timespec?*abstime);??int?pthread_cond_destroy(pthread_cond_t?*cond);??int?pthread_cond_signal(pthread_cond_t?*cond);??int?pthread_cond_broadcast(pthread_cond_t?*cond);???? ?
(1)初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER(前者為動態初始化,后者為靜態初始化);屬性置為NULL
(2)等待條件成立.pthread_wait,pthread_timewait.wait()釋放鎖,并阻塞等待條件變量為真,timewait()設置等待時間,仍未signal,返回ETIMEOUT(加鎖保證只有一個線程wait)
(3)激活條件變量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待線程)
(4)清除條件變量:destroy;無線程等待,否則返回EBUSY
對于
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
一定要在mutex的鎖定區域內使用。
? ? 如果要正確的使用pthread_mutex_lock與pthread_mutex_unlock,請參考
pthread_cleanup_push和pthread_cleanup_pop宏,它能夠在線程被cancel的時候正確的釋放mutex!
? ? 另外,posix1標準說,pthread_cond_signal與pthread_cond_broadcast無需考慮調用線程是否是mutex的擁有者,也就是說,可以在lock與unlock以外的區域調用。如果我們對調用行為不關心,那么請在lock區域之外調用吧。
說明:
? ? (1)pthread_cond_wait 自動解鎖互斥量(如同執行了pthread_unlock_mutex),并等待條件變量觸發。這時線程掛起,不占用CPU時間,直到條件變量被觸發(變量為ture)。在調用 pthread_cond_wait之前,應用程序必須加鎖互斥量。pthread_cond_wait函數返回前,自動重新對互斥量加鎖(如同執行了pthread_lock_mutex)。
? ? (2)互斥量的解鎖和在條件變量上掛起都是自動進行的。因此,在條件變量被觸發前,如果所有的線程都要對互斥量加鎖,這種機制可保證在線程加鎖互斥量和進入等待條件變量期間,條件變量不被觸發。條件變量要和互斥量相聯結,以避免出現條件競爭——個線程預備等待一個條件變量,當它在真正進入等待之前,另一個線程恰好觸發了該條件(條件滿足信號有可能在測試條件和調用pthread_cond_wait函數(block)之間被發出,從而造成無限制的等待)。
(3)pthread_cond_timedwait 和 pthread_cond_wait 一樣,自動解鎖互斥量及等待條件變量,但它還限定了等待時間。如果在abstime指定的時間內cond未觸發,互斥量mutex被重新加鎖,且pthread_cond_timedwait返回錯誤 ETIMEDOUT。abstime 參數指定一個絕對時間,時間原點與 time 和 gettimeofday 相同:abstime = 0 表示 1970年1月1日00:00:00 GMT。
(4)pthread_cond_destroy 銷毀一個條件變量,釋放它擁有的資源。進入 pthread_cond_destroy 之前,必須沒有在該條件變量上等待的線程。
? ? (5)條件變量函數不是異步信號安全的,不應當在信號處理程序中進行調用。特別要注意,如果在信號處理程序中調用 pthread_cond_signal 或 pthread_cond_boardcast 函數,可能導致調用線程死鎖。
示例程序1
Cpp代碼 ?
#include?<stdio.h>??#include?<pthread.h>??#include?"stdlib.h"??#include?"unistd.h"?????pthread_mutex_t?mutex;??pthread_cond_t?cond;?????void?hander(void?*arg)??{??????free(arg);???????(void)pthread_mutex_unlock(&mutex);??}?????void?*thread1(void?*arg)??{???????pthread_cleanup_push(hander,?&mutex);????????while(1)????????{????????????printf("thread1?is?running\n");????????????pthread_mutex_lock(&mutex);????????????pthread_cond_wait(&cond,&mutex);????????????printf("thread1?applied?the?condition\n");????????????pthread_mutex_unlock(&mutex);????????????sleep(4);????????}????????pthread_cleanup_pop(0);???}??????void?*thread2(void?*arg)??{???????while(1)???????{???????????printf("thread2?is?running\n");???????????pthread_mutex_lock(&mutex);???????????pthread_cond_wait(&cond,&mutex);???????????printf("thread2?applied?the?condition\n");???????????pthread_mutex_unlock(&mutex);???????????sleep(1);???????}??}?????int?main()??{???????pthread_t?thid1,thid2;????????printf("condition?variable?study!\n");????????pthread_mutex_init(&mutex,NULL);????????pthread_cond_init(&cond,NULL);????????pthread_create(&thid1,NULL,thread1,NULL);????????pthread_create(&thid2,NULL,thread2,NULL);????????sleep(1);????????do????????{????????????pthread_cond_signal(&cond);????????}while(1);????????sleep(20);????????pthread_exit(0);????????return?0;??}?? ??
?
示例程序2:
Cpp代碼 ?
#include?<pthread.h>???#include?<unistd.h>???#include?"stdio.h"??#include?"stdlib.h"?????static?pthread_mutex_t?mtx?=?PTHREAD_MUTEX_INITIALIZER;???static?pthread_cond_t?cond?=?PTHREAD_COND_INITIALIZER;??????struct?node???{???????int?n_number;????????struct?node?*n_next;???}?*head?=?NULL;?????????static?void?cleanup_handler(void?*arg)???{???????printf("Cleanup?handler?of?second?thread./n");????????free(arg);????????(void)pthread_mutex_unlock(&mtx);???}??????static?void?*thread_func(void?*arg)???{???????struct?node?*p?=?NULL;????????pthread_cleanup_push(cleanup_handler,?p);????????while?(1)????????{???????????????????????pthread_mutex_lock(&mtx);????????????while?(head?==?NULL)????????????{????????????????????????????????????????????????????????????????????????????????????????????????????pthread_cond_wait(&cond,?&mtx);????????????p?=?head;????????????head?=?head->n_next;????????????printf("Got?%d?from?front?of?queue/n",?p->n_number);???????????free(p);?????????????}?????????????pthread_mutex_unlock(&mtx);????????}????????pthread_cleanup_pop(0);????????return?0;???}??????int?main(void)???{???????pthread_t?tid;????????int?i;????????struct?node?*p;??????????????????????pthread_create(&tid,?NULL,?thread_func,?NULL);????????sleep(1);????????for?(i?=?0;?i?<?10;?i++)????????{????????????p?=?(struct?node*)malloc(sizeof(struct?node));????????????p->n_number?=?i;????????????pthread_mutex_lock(&mtx);????????????p->n_next?=?head;????????????head?=?p;????????????pthread_cond_signal(&cond);????????????pthread_mutex_unlock(&mtx);????????????sleep(1);????????}????????printf("thread?1?wanna?end?the?line.So?cancel?thread?2./n");?????????????????????????????pthread_cancel(tid);????????pthread_join(tid,?NULL);????????printf("All?done?--?exiting/n");????????return?0;???}?? ?
3)信號量
? ? 如同進程一樣,線程也可以通過信號量來實現通信,雖然是輕量級的。
? ? 信號量函數的名字都以"sem_"打頭。線程使用的基本信號量函數有四個。
#include <semaphore.h>
int sem_init (sem_t *sem , int pshared, unsigned int value);
? ? 這是對由sem指定的信號量進行初始化,設置好它的共享選項(linux 只支持為0,即表示它是當前進程的局部信號量),然后給它一個初始值VALUE。
兩個原子操作函數:
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
? ? 這兩個函數都要用一個由sem_init調用初始化的信號量對象的指針做參數。
sem_post:給信號量的值加1;
sem_wait:給信號量減1;對一個值為0的信號量調用sem_wait,這個函數將會等待直到有其它線程使它不再是0為止。
int sem_destroy(sem_t *sem);
? ? 這個函數的作用是再我們用完信號量后都它進行清理。歸還自己占有的一切資源。
?
示例代碼:
Cpp代碼 ?
#include?<stdlib.h>???#include?<stdio.h>???#include?<unistd.h>???#include?<pthread.h>???#include?<semaphore.h>???#include?<errno.h>?????????#define?return_if_fail(p)?if((p)?==?0){printf?("[%s]:func?error!/n",?__func__);return;}?????????typedef?struct?_PrivInfo???{?????sem_t?s1;?????sem_t?s2;?????time_t?end_time;???}PrivInfo;?????????static?void?info_init?(PrivInfo*?thiz);???static?void?info_destroy?(PrivInfo*?thiz);???static?void*?pthread_func_1?(PrivInfo*?thiz);???static?void*?pthread_func_2?(PrivInfo*?thiz);?????????int?main?(int?argc,?char**?argv)???{?????pthread_t?pt_1?=?0;?????pthread_t?pt_2?=?0;?????int?ret?=?0;?????PrivInfo*?thiz?=?NULL;?????????????thiz?=?(PrivInfo*?)malloc?(sizeof?(PrivInfo));?????if?(thiz?==?NULL)?????{???????printf?("[%s]:?Failed?to?malloc?priv./n");???????return?-1;?????}???????????info_init?(thiz);???????????ret?=?pthread_create?(&pt_1,?NULL,?(void*)pthread_func_1,?thiz);?????if?(ret?!=?0)?????{???????perror?("pthread_1_create:");?????}???????????ret?=?pthread_create?(&pt_2,?NULL,?(void*)pthread_func_2,?thiz);?????if?(ret?!=?0)?????{????????perror?("pthread_2_create:");?????}???????????pthread_join?(pt_1,?NULL);?????pthread_join?(pt_2,?NULL);???????????info_destroy?(thiz);?????????????return?0;???}?????????static?void?info_init?(PrivInfo*?thiz)???{?????return_if_fail?(thiz?!=?NULL);???????????thiz->end_time?=?time(NULL)?+?10;?????????????sem_init?(&thiz->s1,?0,?1);?????sem_init?(&thiz->s2,?0,?0);???????????return;???}?????????static?void?info_destroy?(PrivInfo*?thiz)???{?????return_if_fail?(thiz?!=?NULL);???????????sem_destroy?(&thiz->s1);?????sem_destroy?(&thiz->s2);???????????free?(thiz);?????thiz?=?NULL;???????????return;???}?????????static?void*?pthread_func_1?(PrivInfo*?thiz)???{?????return_if_fail?(thiz?!=?NULL);???????????while?(time(NULL)?<?thiz->end_time)?????{???????sem_wait?(&thiz->s2);???????printf?("pthread1:?pthread1?get?the?lock./n");?????????????sem_post?(&thiz->s1);???????printf?("pthread1:?pthread1?unlock/n");?????????????sleep?(1);?????}???????????return;???}?????????static?void*?pthread_func_2?(PrivInfo*?thiz)???{?????return_if_fail?(thiz?!=?NULL);???????????while?(time?(NULL)?<?thiz->end_time)?????{???????sem_wait?(&thiz->s1);???????printf?("pthread2:?pthread2?get?the?unlock./n");?????????????sem_post?(&thiz->s2);???????printf?("pthread2:?pthread2?unlock./n");?????????????sleep?(1);?????}???????????return;???}?? ??
?
通 過執行結果后,可以看出,會先執行線程二的函數,然后再執行線程一的函數。它們兩就實現了同步。在上大學的時候,雖然對這些概念知道,可都沒有實踐過,所 以有時候時間一久就會模糊甚至忘記,到了工作如果還保持這么一種狀態,那就太可怕了。雖然現在外面的技術在不斷的變化更新,可是不管怎么變,其核心技術還 是依舊的,所以我們必須要打好自己的基礎,再學習其他新的知識,那時候再學新的知識也會覺得比較簡單的。信號量代碼摘自http://blog.csdn.net/wtz1985/article/details/3835781
參考:
【1】 http://www.cnblogs.com/feisky/archive/2009/11/12/1601824.html
【2】 http://www.cnblogs.com/mydomain/archive/2011/07/10/2102147.html
【3】 線程函數介紹
http://www.unix.org/version2/whatsnew/threadsref.html
【4】 http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
【5】 線程常用函數簡介
http://www.rosoo.net/a/201004/8954.html
【6】 條件變量
http://blog.csdn.net/hiflower/article/details/2195350
【7】條件變量函數說明
http://blog.csdn.net/hairetz/article/details/4535920
總結
以上是生活随笔為你收集整理的Linux多线程同步的几种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。