linux线程(互斥锁、条件)
線程概念:
-
典型的UNIX/Linux進程可以看成只有一個控制線程:一個進程在同一時刻只做一件事情。有了多個控制線程后,在程序設計時可以把進程設計成在同一時刻做不止一件事,每個線程各自處理獨立的任務。
-
進程是程序執行時的一個實例,是擔當分配系統資源(CPU時間、內存等)的基本單位。在面向線程設計的系統中,進程本身不是基本運行單位,而是線程的容器。程序本身只是指令、數據及其組織形式的描述,進程才是程序(那些指令和數據)的真正運行實例。
-
首先Linux并不存在真正的線程,Linux的線程是使用進程模擬的。當我們需要在一個進程中同時運行多個執行流時,我們并不可以開辟多個進程執行我們的操作(32位機器里每個進程認為它獨享4G的內存資源),此時便引入了線程,例如當我們既需要下載內容,又需要瀏覽網頁時,此時多線程便起了作用。線程是承擔調度的基本單位,一個進程可擁有多個線程,它的執行力度比進程更加細致,線程資源共享。
-
“進程——資源分配的最小單位,線程——程序執行的最小單位”
-
一個進程至少包含一個線程,進程是運行的程序,程序是靜態的概念,進程是動態的概念。
-
進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對于一些要求同時進行并且又要共享某些變量的并發操作,可以用線程,也可以用進程間的通信。
使用線程的理由:
- 總的來說就是:進程有獨立的地址空間,線程沒有單獨的地址空間(同一進程內的線程共享進程的地址空間)。
- 使用多線程的理由之一是和進程相比,它是一種非常"節儉"的多任務操作方式。我們知道,在Linux系統下,啟動一個新的進程必須分配給它獨立的地址空間,建立眾多的數據表來維護它的代碼段、堆棧段和數據段,這是一種"昂貴"的多任務工作方式。而運行于一個進程中的多個線程,它們彼此之間使用相同的地址空間,共享大部分數據,啟動一個線程所花費的空間遠遠小于啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小于進程間切換所需要的時間。據統計,總的說來,一個進程的開銷大約是一個線程開銷的30倍左右,當然,在具體的系統上,這個數據可能會有較大的區別。
- 使用多線程的理由之二是線程間方便的通信機制。對不同進程來說,它們具有獨立的數據空間,要進行數據的傳遞只能通過通信的方式進行,這種方式不僅費時,而且很不方便。線程則不然,由于同一進程下的線程之間共享數據空間,所以一個線程的數據可以直接為其它線程所用,這不僅快捷,而且方便。當然,數據的共享也帶來其他一些問題,有的變量不能同時被兩個線程所修改,有的子程序中聲明為static的數據更有可能給多線程程序帶來災難性的打擊,這些正是編寫多線程程序時最需要注意的地方。
除了以上所說的優點外,不和進程比較,多線程程序作為一種多任務、并發的工作方式,當然有以下的優點:
- 提高應用程序響應。這對圖形界面的程序尤其有意義,當一個操作耗時很長時,整個系統都會等待這個操作,此時程序不會響應鍵盤、鼠標、菜單的操作,而使用多線程技術,將耗時長的操作(time consuming)置于一個新的線程,可以避免這種尷尬的情況。
- 使多CPU系統更加有效。操作系統會保證當線程數不大于CPU數目時,不同的線程運行于不同的CPU上。
- 改善程序結構。一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程序會利于理解和修改。
POSIX線程庫:與線程有關的函數構成了一個完整的系列,絕大多數函數的名字都是以“pthread_”打頭的要使用這些函數庫,要通過引入頭文件#include<pthread.h>鏈接這些線程函數庫時要使用編譯器命令的“-lpthread”選項,多線程開發在 Linux 平臺上已經有成熟的 pthread 庫支持。
其涉及的多線程開發的最基本概念主要包含三點:線程,互斥鎖,條件。其中,線程操作又分線程的創建,退出,等待 3 種。互斥鎖則包括 4 種操作,分別是創建,銷毀,加鎖和解鎖。條件操作有 5 種操作:創建,銷毀,觸發,廣播和等待。
線程開發API:
線程的創建:pthread_create()函數
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg); 功能:創建一個新的線程 參數:thread:指向的內存單元被設置為新創建線程的線程IDattr:設置線程的屬性,attr為NULL表?示使?用默認屬性start_routine:新創建的線程從start_rtn函數的地址開始運行該函數只有一個萬能 指針參數arg,如果需要向start_rtn函數傳遞的參數不止一個那么需要把這些參數放到一個結構中,然后把這個結構的地址作為arg的參數傳入。 arg:傳給線程啟動函數的參數返回值:成功返回0;失敗返回錯誤碼 注意:void*表示指針類型不限制,只要是指針就行 因為pthread并非Linux系統的默認庫,而是POSIX線程庫。 在Linux中將其作為一個庫來使用,因此加上 -lpthread(或-pthread)以顯式鏈接該庫。 函數在執行錯誤時的錯誤信息將作為返回值返回,并不修改系統全局變量errno,當然也無法使用perror()打印錯誤信息。線程退出:pthread_exit()函數
#include <pthread.h> void pthread_exit(void *retval); 功能:使用函數pthread_exit退出線程,這是線程的主動行為;由于一個進程中的多個線程是共享數據段的因此通常在線程退出之后,退出線程所占用的資源并不會隨著線程的終止而得到釋放,但是可以用pthread_join()函數來同步并釋放資源。 參數:retval: pthread_exit()調用線程的返回值,可由其他函數如pthread_join來檢索獲取。單個線程可以通過以下三種方式退出,在不終止整個進程的情況下停止它的控制流:1)線程只是從啟動例程中返回,返回值是線程的退出碼。2)線程可以被同一進程中的其他線程取消一個線程可以調用pthread_cancel終止同一進程中的另一個線程。3)線程調用pthread_exit: 注意:調用exit()的話,主進程會終止從線程函數return,這種方法法對主線程不適用從main函數return相當于調用exit。線程等待:pthread_join()函數
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);功能:調pthread_join()函數,以阻塞的方式等待thread指定的線程結束。當函數返回時,被等待線程的資源被收回。如果線程已經結束,那么該函數會立即返回。并且thread指定的線程必須是joinable的。 參數:thread: 線程標識符,即線程ID,標識唯一線程。retval: 用戶定義的指針,用來存儲被等待線程的返回值。退出函數返回的是一個空指針類型,接受函數也必須用一個指針來接收。但是函數給出的參數是接收指針的地址,即,接收到的指針值寫入給出的地址處的指針變量。返回值:若成功返回0,否則返回錯誤編號為什么要用這個函數?1、代碼中如果沒有pthread_join,主線程會很快結束從而使整個進程結束,從而使創建的線程沒有機會開始執行就結束了。2、在很多情況下,主線程生成并起動了子線程,如果子線程里要進行大量的耗時的運算主線程往往將于子線程之前結束,但是如果主線程處理完其他的事務后,需要用到子線程的處理結果也就是主線程需要等待子線程執行完成之后再結束,這個時候就要用到pthread_join()方法了。即pthread_join()的作用可以這樣理解:主線程等待子線程的終止。也就是在子線程調用了pthread_join()方法后面的代碼只有等到子線程結束了才能執行。信號發送函數:pthread_cancel()
#include <pthread.h> int pthread_cancel(pthread_t thread) 功能:發送終止信號給thread線程 參數:thread:要發送的目標線程id返回值:如果成功則返回0,否則為非0值。函數說明:pyhread_cancel函數只是給線程發送了一個請求該請求是希望可以將該線程終止。所以對于該請求的話,只是對于線程的一個建議線程也可能就不會立即終止,會繼續運行,直到運行到取消點的時候該線程才會退出取消點的理解:
- 取消點是線程檢查是否被取消并按照請求進行動作的一個位置.
- 取消點是如何出現的呢?
- 對于取消點對于使用某些函數就會出現取消點
- 例如:sleep,wait,waitpid,waitid,send等函數
設置取消的狀態函數:pthread_setcancelstate()
#include <pthread.h> pthread_setcancelstate(int state, int* oldstate); 功能:這個函數可以設置取消的狀態,有兩種狀態PTHREAD_CANCEL_ENABLE(可取消狀態)PTHREAD_CANCEL_DISABLE(不可取消狀態)。 參數:state:將當前狀態改為stateoldstate:將該線程原先的狀態放到oldtype所指向的空間里面 返回值:成功返回0,失敗返回錯誤碼這兩步是一個原子操作。對于pthread_cancel函數默認的是PTHREAD_CANCEL_ENABLE可取消狀態。當狀態設為PTHREAD_CANCEL_DISABLE時,對于pthread_cancel的調用并不會殺死線程。相反,該取消請求對于這個線程還處于掛起狀態(也就是未決),直到線程的取消狀態變為PTHREAD_CANCEL_ENABLE時線程將在下一個取消點上對所有的掛起請求進行處理。設置取消類型函數:pthread_setcanceltype()
#include <thread.h> int pthread_testcanceltype(int type, int* oldtype);參數:type:將取消的類型設置為typeoldtype:將該線程的取消類型放到oldtype所指向的空間里面返回值:成功返回0,失敗返回錯誤碼1、我們默認的取消類型是推遲取消。也就是會運行到取消點再取消對于可以設置的取消類型有PTHREAD_CANCEL_DEFERRED(推遲取消),也就是默認的取消類型。 2、PTHRAED_CANCEL_ASYNCHRONOUS(異步取消),采用異步取消之后,線程可以在任意時間取消,不是非得到取消點才可以取消。加取消點函數:pthread_testcancel()
- 對于一個線程沒有調用上面說的可以產生取消點函數,那么該線程就沒有取消點,也就無法被取消,取消請求也就會一直掛起,不會被線程處理。
- 所以如果要對線程進行取消請求的話,可以自己給線程加上取消點
- pthread_testcancel函數就可以在程序自己加上取消點。
線程脫離:pthread_detach()函數
一個線程或者是可匯合(joinable,默認值),或者是脫離的(detached)。當一個可匯合的線程終止時,它的線程ID和退出狀態將留存到另一個線程對它調用pthread_join。脫離的線程卻像守護進程,當它們終止時,所有相關的資源都被釋放,我們不能等待它們終止。
線程分離狀態: 指定該狀態,線程主動與主控線程斷開關系。線程結束后(不會產生僵尸線程),其退出狀態不由其他線程獲取,而直接自己自動釋放(自己清理掉PCB的殘留資源)。網絡、多線程服務器常用。
- 進程若有該機制,將不會產生僵尸進程。僵尸進程的產生主要由于進程死后,大部分資源被釋放,一點殘留資源仍存于系統中,導致內核認為該進程仍存在。(注意進程沒有這一機制)
- 也可使用 pthread_create函數參2(線程屬性)來設置線程分離。
- 一般情況下,線程終止后,其終止狀態一直保留到其它線程調用pthread_join獲取它的狀態為止(或者進程終止被回收了)。但是線程也可以被置為detach狀態,這樣的線程一旦終止就立刻回收它占用的所有資源,而不保留終止狀態。不能對一個已經處于detach狀態的線程調用pthread_join,這樣的調用將返回EINVAL錯誤(22號錯誤)。也就是說,如果已經對一個線程調用了pthread_detach就不能再調用pthread_join了。
線程ID獲取:pthread_self()函數
#include <pthread.h> pthread_t pthread_self(void);返回:調用線程的ID,可以和pthread_detach函數配合使用線程ID比較:pthread_equal()函數
#include <pthread.h> int pthread_equal(pthread_t tid1, pthread_t tid2);返回:若相等則返回非0值,否則返回0pthread_create()函數、pthread_exit()函數和pthread_join()函數搭配實例:
#include<stdio.h> #include <pthread.h> #include<stdlib.h> struct num {int a,b; }; void *add(void* arg)//函數多個參數時用結構體 {struct num *p;p=(struct num*)malloc(128);p->a=12;p->b=18;//這種方式是在堆上面開辟了空間,如果不是手動釋放將一直存在//但是在得到數據后最好將內存釋放否則可能造成內存泄露/*struct num p;p.a=1;p.b=2;這種方式傳參會在函數調用時將內存空間釋放,得不到準確的值*/printf("a=%d\n",((struct num*)arg)->a);printf("b=%d\n",((struct num*)arg)->b);printf("a+b=%d\n",((struct num*)arg)->a+((struct num*)arg)->b);printf("son id=%ld\n",(unsigned long)pthread_self());pthread_exit((void*)&p); } int main() {pthread_t td;int retnu;struct num son;struct num* sonret;son.a=10;son.b=-3;retnu=pthread_create(&td,NULL,add,(void*)&son);if(retnu!=0){printf("pthread create fail\n");}pthread_join(td,(void**)&sonret);//退出函數返回的是一個空指針類型,接受函數也必須用一個指針來接收。//但是函數給出的參數是接收指針的地址,即,接收到的指針值寫入給出的地址處的指針變量。printf("進程結束\n");printf("son return:a=%d,b=%d\n",((struct num*)sonret)->a,((struct num*)sonret)->b);printf("main id=%ld,td=%ld\n",(unsigned long)pthread_self(),(unsigned long)td);return 0; }實例驗證子線程和主線程共享資源:
#include<stdio.h> #include <pthread.h> int i=1; void * func1(void*arg) {i=i+1;printf("thread1 i=%d\n",i);pthread_exit((void*)&i); } void * func2(void*arg) {i=i+1;printf("thread2 i=%d\n",i);pthread_exit((void*)&i); } int main() {pthread_t t1;pthread_t t2;int t1return;int t2return;int* th1;int* th2;t1return=pthread_create(&t1,NULL,func1,(void*)&i);if(t1return!=0){printf("pthread create fail\n");}pthread_join(t1,(void**)&th1);printf("thread1 return:i=%d\n",*(int *)th1);t2return=pthread_create(&t2,NULL,func2,(void*)&i);if(t2return!=0){printf("pthread create fail\n");}pthread_join(t2,(void**)&th2);printf("thread2 return:i=%d\n",*(int *)th2);printf("main thread i=%d\n",i);return 0; }對于多線程程序來說,我們往往需要對這些多線程進行同步。同步(synchronization)是指在一定的時間內只允許某一個線程訪問某個資源。而在此時間內,不允許其它的線程訪問該資源。我們可以通過互斥鎖(mutex),條件變量(condition variable)和讀寫鎖(reader-writer lock)來同步資源。
互斥鎖:
- 互斥量(mutex)從本質上來說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成后釋放互斥量上的鎖。對互斥量進行加鎖后,任何其他試圖再次對互斥量加鎖的線程將會被阻塞直到當前線程釋放該互斥鎖。如果釋放互斥鎖時有多個線程阻塞,所有在該互斥鎖上的阻塞線程都會變成可運行狀態,第一個變為可運行狀態的線程可以對互斥量加鎖,其他線程將會看到互斥鎖依然被鎖住,只能回去等待它重新變為可用。在這種方式下,每次只有一個線程可以向前運行。
- 在設計時需要規定所有的線程必須遵守相同的數據訪問規則。只有這樣,互斥機制才能正常工作。操作系統并不會做數據訪問的串行化。如果允許其中的某個線程在沒有得到鎖的情況下也可以訪問共享資源,那么即使其它的線程在使用共享資源前都獲取了鎖,也還是會出現數據不一致的問題。
- 互斥變量用pthread_mutex_t數據類型表示。在使用互斥變量前必須對它進行初始化,可以把它置為常量PTHREAD_MUTEX_INITIALIZER(只對靜態分配的互斥量),也可以通過調用pthread_mutex_init函數進行初始化。如果動態地分配互斥量(例如通過調用malloc函數),那么在釋放內存前需要調用pthread_mutex_destroy。
- 在使用互斥鎖前,需要定義互斥鎖(全局變量),定義互斥鎖對象形式為:pthread_mutex_t lock;
- 還可以用宏 PTHREAD_MUTEX_INITIALIZER 來初始化靜態分配的互斥鎖,如下:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- 使用互斥鎖能保證在一個線程內的代碼跑完后再去跑別的線程的代碼。
創建互斥鎖:
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 功能:該函數用于C函數的多線程編程中,互斥鎖的初始化。pthread_mutex_init()函數是以動態方式創建互斥鎖的。 參數:mutex 是指向要初始化的互斥鎖的指針。參數attr指定了新建互斥鎖的屬性。如果參數attr為NULL則使用默認的互斥鎖屬性,默認屬性為快速互斥鎖 。互斥鎖的屬性在創建鎖的時候指定,在LinuxThreads實現中僅有一個鎖類型屬性不同的鎖類型在試圖對一個已經被鎖定的互斥鎖加鎖時表現不同。返回值:函數成功完成之后會返回零,其他任何返回值都表示出現了錯誤。函數成功執行后,互斥鎖被初始化為鎖住態。互斥鎖屬性:
互斥鎖的屬性在創建鎖的時候指定,在LinuxThreads實現中僅有一個鎖類型屬性,不同的鎖類型在試圖對一個已經被鎖定的互斥鎖加鎖時表現不同。當前(glibc2.2.3,linuxthreads0.9)有四個值可供選擇:
- PTHREAD_MUTEX_TIMED_NP,這是缺省值,也就是普通鎖。當一個線程加鎖以后,其余請求鎖的線程將形成一個等待隊列,并在解鎖后按優先級獲得鎖。這種鎖策略保證了資源分配的公平性。
- PTHREAD_MUTEX_RECURSIVE_NP,嵌套鎖,允許同一個線程對同一個鎖成功獲得多次,并通過多次unlock解鎖。如果是不同線程請求,則在加鎖線程解鎖時重新競爭。
- PTHREAD_MUTEX_ERRORCHECK_NP,檢錯鎖,如果同一個線程請求同一個鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動作相同。這樣就保證當不允許多次加鎖時不會出現最簡單情況下的死鎖。
- PTHREAD_MUTEX_ADAPTIVE_NP,適應鎖,動作最簡單的鎖類型,僅等待解鎖后重新競爭。
互斥鎖銷毀:pthread_mutex_destroy()
#include <pthread.h> int pthread_mutex_destroy( pthread_mutex_t * mutex); 參數:指向要初始化的互斥鎖的指針 返回值:成功后都返回 0,否則返回錯誤編號以指名錯誤。加鎖函數:pthread_mutex_lock()
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t* mutex); int pthread_mutex_trylock(pthread_mutex_t* mutex); 功能:描述 pthread_mutex_lock()函數鎖住由mutex指定的mutex 對象。pthread_mutex_trylock()調用在參數mutex指定的mutex對象當前被鎖住的時候立即返回除此之外,pthread_mutex_trylock()跟pthread_mutex_lock()功能完全一樣。參數:指向要操作的的mutex對象返回值:pthread_mutex_lock() 成功:返回0,否則返回一個錯誤的提示碼 pthread_mutex_trylock() 在成功獲得了一個mutex的鎖后返回0,否則返回一個錯誤提示碼錯誤解鎖函數:pthread_mutex_unlock()
#include <pthread.h> int pthread_mutex_unlock(pthread_mutex_t* mutex); 參數:指向要操作的的mutex對象 返回值: 成功則返回0, 出錯則返回錯誤編號.函數的綜合應用:
#include<stdio.h> #include <pthread.h> #include <unistd.h> int i=1; pthread_mutex_t mutex; void * func1(void*arg) {pthread_mutex_lock(&mutex);while(1){i=i+1;printf("thread1 i=%d\n",i);sleep(2);if(i==7){pthread_mutex_unlock(&mutex);pthread_exit((void*)&i);}} } void * func2(void*arg) {pthread_mutex_lock(&mutex);while(1){i=i+1;printf("thread2 i=%d\n",i);sleep(2);if(i==13){pthread_mutex_unlock(&mutex);pthread_exit((void*)&i);}}} } int main() {pthread_t t1;pthread_t t2;int t1return;int t2return;int* th1;int* th2;pthread_mutex_init(&mutex,NULL);t1return=pthread_create(&t1,NULL,func1,(void*)&i);if(t1return!=0){printf("pthread create fail\n");}sleep(2);pthread_mutex_lock(&mutex);for(;;){i++;printf("main thread i=%d\n",i);sleep(2);if(i==10){pthread_mutex_unlock(&mutex);break;}}pthread_join(t1,(void**)&th1);printf("thread1 return:i=%d\n",*(int *)th1);t2return=pthread_create(&t2,NULL,func2,(void*)&i);if(t2return!=0){printf("pthread create fail\n");}pthread_join(t2,(void**)&th2);printf("thread2 return:i=%d\n",*(int *)th2);pthread_mutex_destroy(&mutex);printf("main over\n");return 0; } 以下是程序每次執行的順序: thread1 i=2 thread1 i=3 thread1 i=4 thread1 i=5 thread1 i=6 thread1 i=7 main thread i=8 main thread i=9 main thread i=10 thread1 return:i=10 thread2 i=11 thread2 i=12 thread2 i=13 thread2 return:i=13 main over通過chmod改變文件權限
可以寫一個腳本來驗證上述代碼,只需要將寫好的文件加上可執行權限即可。
什么條件下可能造成死鎖:
- 在線程一里面想先獲取鎖一,然后獲取鎖二,在線程二里面想先獲取鎖二再獲取鎖一,如果在線程一獲取鎖一并且沒有解鎖時,同時未獲得鎖二,如果此時線程二獲得了鎖二那么將造成死鎖。
條件變量:
- 條件變量是線程另一可用的同步機制。條件變量給多個線程提供了一個會合的場所。條件變量與互斥量一起使用時,允許線程以無競爭的方式等待特定的條件發生。
- 條件本身是由互斥量保護的。線程在改變條件狀態前必須首先鎖住互斥量,其他線程在獲得互斥量之前不會察覺到這種改變,因為必須鎖定互斥量以后才能計算條件。
- 條件變量使用之前必須首先初始化,pthread_cond_t數據類型代表的條件變量可以用兩種方式進行初始化,可以把常量PTHREAD_COND_INITIALIZER賦給靜態分配的條件變量,但是如果條件變量是動態分配的,可以使用pthread_cond_destroy函數對條件變量進行去除初始化(deinitialize)。
- 注意: 不能用多個線程初始化同一個條件變量,當一個線程要使用條件變量的時候確保它是未被使用的。
創建及銷毀條件變量:
#include <pthread.h> int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); int pthread_cond_destroy(pthread_cond_t*cond);注銷一個條件變量需要調用pthread_cond_destroy() 只有在沒有線程在該條件變量上等待的時候才能注銷這個條件變量,否則返回EBUSY。參數:cond:指向定義的cond變量參數attr為空指針時,函數創建的是一個缺省的條件變量。否則條件變量的屬性將由attr中的屬性值來決定返回值:若成功執行,函數pthread_cond_init()將返回零,并把新建條件變量的id放在cond變量中否則,將返回一個代表錯誤的錯誤碼。等待:
- 等待條件有兩種方式:無條件等待pthread_cond_wait()和計時等待pthread_cond_timedwait()
- 其中計時等待方式如果在給定時刻前條件沒有滿足,則返回ETIMEOUT,結束等待
- 無論哪種等待方式,都必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的競爭條件(Race Condition)。mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應鎖(PTHREAD_MUTEX_ADAPTIVE_NP),且在調用pthread_cond_wait()前必須由本線程加鎖(pthread_mutex_lock()),而在更新條件等待隊列以前,mutex保持鎖定狀態,并在線程掛起進入等待前解鎖。在條件滿足從而離開pthread_cond_wait()之前,mutex將被重新加鎖,以與進入pthread_cond_wait()前的加鎖動作對應。
- 激發條件有兩種形式,pthread_cond_signal()激活一個等待該條件的線程,存在多個等待線程時按入隊順序激活其中一個;而pthread_cond_broadcast()則激活所有等待線程。
觸發:
#include <pthread.h> int pthread_cond_signal(pthread_cond_t* cond); int pthread_cond_broadcast(pthread_cond_t* cond);返回:若成功返回0,否則返回錯誤編號這兩個函數可以用于通知線程條件已經滿足。pthread_cond_signal函數將喚醒等待該條件的某個線程 而pthread_cond_broadcast函數將喚醒等待該條件的所有進程。互斥鎖和條件變量綜合使用:
#include<stdio.h> #include <pthread.h> #include <unistd.h> int i=0; pthread_cond_t cond; pthread_mutex_t mutex; void * func1(void*arg) {printf("線程一運行\n");while(1){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);if(i==7){printf("新的循環\n");i=0;pthread_mutex_unlock(&mutex);}}pthread_exit((void*)&i); } void * func2(void*arg) {printf("線程二運行\n");while(1){pthread_mutex_lock(&mutex);i=i+1;printf("t2:i=%d\n",i);if(i==7){pthread_cond_signal(&cond);//break;}pthread_mutex_unlock(&mutex);sleep(1);}pthread_exit((void*)&i); } int main() {pthread_t t1;pthread_t t2;int t1return;int t2return;int* th1;int* th2;pthread_cond_init(&cond,NULL);pthread_mutex_init(&mutex,NULL);t1return=pthread_create(&t1,NULL,func1,(void*)&i);if(t1return!=0){printf("pthread create fail\n");}t2return=pthread_create(&t2,NULL,func2,(void*)&i);if(t2return!=0){printf("pthread create fail\n");}pthread_join(t1,(void**)&th1);pthread_join(t2,(void**)&th2);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);printf("main over\n");return 0; }將程序輸出到文件:
fhn@ubuntu:~/thread$ ./test >> result.txt & [2] 18838 其中>>表示將運行的結果追加到test.ret.txt中 &表示在后臺運行 [2] 18838(這是test 進程的ID號)大佬博客:條件變量和互斥鎖使用實例
學習過程補充:
- C中struct只是類型聲明,沒有內存空間的分配,而static變量是需要分配內存的。這種解釋可以說明為什么在結構體內包含static變量會出錯。
- void (*add)(void);這個只是用來聲明add是函數指針的,不能用來函數定義,這個add可以指向其他函數的指針,但是要求函數類型和add的函數類型相同。
- 程序中各種變量、常量的存儲位置
- 關鍵字restrict只用于限定指針,表明本指針是訪問一個數據對象的惟一且初始的方式。
- 生產者消費者問題
- 這是一個非常經典的多線程題目,題目大意如下:有一個生產者在生產產品,這些產品將提供給若干個消費者去消費,為了使生產者和消費者能并發執行,在兩者之間設置一個有多個緩沖區的緩沖池,生產者將它生產的產品放入一個緩沖區中,消費者可以從緩沖區中取走產品進行消費,所有生產者和消費者都是異步方式運行的,但它們必須保持同步,即不允許消費者到一個空的緩沖區中取產品,也不允許生產者向一個已經裝滿產品且尚未被取走的緩沖區中投放產品。
- 消費者將g_count每次減去1,生產者將g_count每次加1;消費者會判斷g_count的大小,如果g_count==0那么消費者線程要阻塞;但是它還會一直占有鎖,所以這樣就阻止了其它線程對g_count的操作;此時我們要用到條件變量;調用pthread_cond_wait(&g_cond, &g_mutex);讓互斥鎖g_mutex在這個g_cond條件上等待;
總結
以上是生活随笔為你收集整理的linux线程(互斥锁、条件)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: phpcms二次开发摘要
- 下一篇: linux网络编程、socket编程