版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第7章ARMLinux多線程開發(fā)實例
7.1
Linux多線程相關API
7.2信號燈
7.3互斥量
7.4條件變量
番禺職業(yè)技術學院番禺職業(yè)技術學院
7.1
Linux多線程相關API
Linux有多線程開發(fā)的Pthread
庫支持。最基本概念:線程、互斥鎖、條件。①線程操作又分線程的創(chuàng)建、退出、等待。②互斥鎖則包括4種操作:創(chuàng)建、銷毀、加鎖和解鎖。③條件操作有5種操作:創(chuàng)建、銷毀、觸發(fā)、廣播和等待。其他的一些線程擴展概念,如信號燈等,都可以通過上面的三個基本元素的基本操作封裝出來。表7.1.線程函數(shù)列表7.1.1線程的創(chuàng)建1.pthread_create函數(shù)pthread_create用于創(chuàng)建一個新的線程。其函數(shù)原型為:#include
<pthread.h>int
pthread_create(pthread_t
*restrict
tidp,const
pthread_attr_t
*restrict
attr,
void
*(*start_rtn)(void),void
*restrict
arg);返回值:若是成功建立線程返回0,否則返回錯誤的編號。函數(shù)說明:第1個參數(shù)為指向要創(chuàng)建的線程的線程id指針,第2個參數(shù)用來設置線程屬性,第3個參數(shù)是線程運行函數(shù)的起始地址,最后一個參數(shù)是運行函數(shù)的參數(shù)。這里,函數(shù)thread不需要參數(shù),所以最后一個參數(shù)設為空指針。第2個參數(shù)也設為空指針,這樣將生成默認屬性的線程。當創(chuàng)建線程成功,函數(shù)返回0,否則失敗,常見的錯誤返回代碼為EAGAIN和EINVAL。EAGAIN表示系統(tǒng)限制創(chuàng)建新的線程,例如線程數(shù)目過多了;EINVAL表示第2個參數(shù)代表的線程屬性值非法。創(chuàng)建線程成功后,新創(chuàng)建的線程則運行參數(shù)三和參數(shù)四確定的函數(shù),原來的線程則繼續(xù)運行下一行代碼。
例7.1線程的創(chuàng)建(gettid.c)#include<pthread.h>voidprintids(constchar*s){printf(“%s
pid:%u
tid:%u\n“,getpid(),pthread_self());}void*thr_fn(void*arg){printf(“newthread:“);}
intmain(){interr;pthread_t
tid;err=pthread_create(&tid,NULL,thr_fn,NULL);if(err!=0)
printf(“can’tcreatethread:%s\n”,strerror(err));printids(“mainthread:”);sleep(1);exit(0);}
進程的編譯需要要加上參數(shù)–lpthread,否則提示找不到函數(shù)的錯誤。具體編譯方法是:gcc–lpthread–ogettid
gettid.c運行結果為mainthread:pid14954tid134529024newthread:pid14954tid1345300487.1.2線程的終止1.pthread_join
函數(shù)pthread_join
函數(shù)用來等待一個線程的結束。其函數(shù)原型為:#include<pthread.h>int
pthread_join(pthread_t
thread,void**rval_ptr);返回值:若函數(shù)調用成功返回0,否則返回錯誤編號。第1個參數(shù)為被等待的線程標識符,第2個參數(shù)為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。這個函數(shù)是一個線程阻塞的函數(shù),調用它的函數(shù)將一直等待到被等待的線程結束為止,當函數(shù)返回時,被等待線程的資源被收回。
2.pthread_exit
函數(shù)
線程是依進程而存在的,當進程終止時,線程也就終止了。當然也有在不終止整個進程的情況下停止它的控制流。(1)線程只是從啟動例程中返回,返回值是線程的退出碼。(2)線程可以被同一進程中的其他線程取消。(3)線程調用pthread_exit.
pthread_exit
:是一個線程的結束。兩種途徑:當函數(shù)結束了,調用它的線程也就結束了;另一種方式是通過函數(shù)pthread_exit
來實現(xiàn)。其函數(shù)原型為:
voidpthread_exit(void*__retval)
唯一的參數(shù)是函數(shù)的返回代碼,只要pthread_join
中的第二個參數(shù)thread_return
不是NULL,這個值將被傳遞給thread_return。需要說明的是,一個線程不能被多個線程等待,否則第一個接收到信號的線程成功返回,其余調用pthread_join
的線程則返回錯誤代碼ESRCH。
例7.2線程終止#include<pthread.h>#include<string.h>void*thr_fn1(void*arg){printf(“thread1returning\n”);return((void*)1);}void*thr_fn2(void*arg){printf(“thread2exiting\n”);return((void*)2);}intmain(){pthread_ttid1,tid2;void*tret;pthread_create(&tid1,NULL,thr_fn1,NULL);pthread_create(&tid2,NULL,thr_fn2,NULL);pthread_join(tid1,&tret);
printf(“thread1exitcode%d\n”,(int)tret);pthread_join(tid2,&tret);
printf(“thread2exitcode%d\n”,(int)tret);exit(0);}運行結果是:thread1returningthread2exitingthread1exitcode1thread2exitcode23.pthread_detach函數(shù)pthread_detach函數(shù)是用于使線程進入分離狀態(tài)。其函數(shù)原型為:#include<pthread.h>int
pthread_detach(pthread_t
tid);返回值:若函數(shù)調用成功返回0,否則返回錯誤編號。在默認情況下,線程的終止狀態(tài)會保存到對該線程調用pthread_join,如果線程已經(jīng)處于分離狀態(tài),線程的底層存儲資源可以在線程終止時立即被收回。當線程被分離時,并不能用pthread_join函數(shù)等待它的終止狀態(tài)。對分離狀態(tài)的線程進行pthread_join的調用會產(chǎn)生失敗,返回EINVAL。
4.pthread_cancel函數(shù)
pthread_cancel函數(shù)是用于取消同一進程中的其他線程。其函數(shù)原型為:#include<pthread.h>int
pthread_cancel(pthread_t
tid);返回值:若函數(shù)調用成功返回0,否則返回錯誤編號。在默認的情況下,pthread_cancel函數(shù)會使由tid標識的線程的行為表現(xiàn)為如同調用了參數(shù)為PTHEAD_CANCELED的pthread_exit函數(shù),但是,線程可以選擇忽略取消方式和控制取消方式。pthread_cancel并不等待線程終止,它僅僅提出請求。5.pthread_cancel_push/pthread_cancel_push_pop函數(shù)pthread_cancel_push/pthread_cancel_pop是線程清理處理程序。其函數(shù)原型為:#include<pthread.h>voidpthread_cancel_push(void(*rtn)(void*),void*arg);voidpthread_cancel_pop(intexecute);參數(shù):rtn為處理程序入口地址,arg為傳遞給處理函數(shù)的參數(shù)。線程可以安排它退出時需要調用的函數(shù),這樣的函數(shù)稱為線程清理處理程序,線程可以建立多個清理處理程序。處理程序記錄在棧中,也就是說它們的執(zhí)行順序與它們注冊時的順序相反。注意:如果線程是通過從他的啟動例程中返回而終止的,它的處理程序就不會調用。還要注意清理處理程序是按照與它們安裝時相反的順序調用的。例7.3線程的取消#include<pthread.h>#include<stdio.h>voidcleanup(void*arg){printf(“cleanup:%s\n”,(char*)arg);}void*thr_fn(void*arg)/*線程入口地址*/{printf(“threadstart\n”);pthread_cleanup_push(cleanup,”threadfirsthandler”);/*設置第一個線程處理程序*/pthread_cleanup_push(cleanup,”thread
secondhandler”);/*設置第二個線程處理程序*/printf(“threadpushcomplete\n”);pthread_cleanup_pop(0);/*取消第一個線程處理程序*/pthread_cleanup_pop(0);/*取消第二個線程處理程序*/}intmain(){pthread_t
tid;void*tret;
pthread_creat(&tid,NULL,thr_fn,(void*)1);/*創(chuàng)建一個線程*/
pthread_join(tid,&tret);/*獲得線程終止狀態(tài)*/
ptinrf(“threadexitcode%d\n”,(int)tret);}7.1.3線程的標識每個進程有一個進程ID,每個線程也有一個線程ID,進程ID在整個系統(tǒng)中是唯一的,但線程不同,線程ID只在它所屬的進程環(huán)境中有效。線程ID用pthread_t數(shù)據(jù)類型來表示,實現(xiàn)的時候可以用一個結構來代表pthread_t數(shù)據(jù)類型,所以可以移植的操作系統(tǒng)不能把它作為整數(shù)處理。1.pthread_self函數(shù)函數(shù)用于獲取自身線程的id。其函數(shù)原型為:#include<pthread.h>pthread_t
pthread_self(void);返回值:調用線程的線程id。2.pthread_equal函數(shù)pthread_equal函數(shù)用于測試兩個線程號ID是否相同。其函數(shù)原型為:#include<pthread.h>int
pthread_equal(pthread_ttid1,pthread_ttid2);返回值:測試兩個線程號ID是否相同。若相等返回非0值,否則返回0。函數(shù)說明:函數(shù)中的參數(shù)tid1為線程1的ID,tid2為線程2的ID。7.1.4線程的一次性初始化在傳統(tǒng)的順序編程中,一次性初始化經(jīng)常通過使用布爾變量來管理??刂谱兞勘混o態(tài)初始化為0,而任何依賴于初始化的代碼都能測試該變量。如果變量值仍然為0,則它能實行初始化,然后將變量置為1。以后檢查的代碼將跳過初始化。在多線程程序設計中,如果多個線程并發(fā)地執(zhí)行初始化序列代碼,2個線程可能發(fā)現(xiàn)控制變量為0,并且都實行初始話,而該過程本該僅僅執(zhí)行一次。初始化的狀態(tài)必須由互斥量保護。如果需要對一個posix變量靜態(tài)的初始化,可使用的方法是用一個互斥量對該變量的初始話進行控制。pthread_once函數(shù)可以對該變量進行動態(tài)初始化。7.1.4線程的一次性初始化如果需要對一個posix變量靜態(tài)的初始化,可使用的方法是用一個互斥量對該變量的初始化進行控制。pthread_once函數(shù)可以對該變量進行動態(tài)初始化。其函數(shù)原理如下:#include<pthread.h>pthread_once_t
once_control=PTHREAD_ONCE_INIT;int
pthread_once(pthread_once_t*once_control,void(*init_routine)(void));、函數(shù)參數(shù):參數(shù)once_control為控制變量,init_routine為初始化函數(shù)。返回值:若函數(shù)調用成功返回0,否則返回錯誤編號。函數(shù)說明:類型為pthread_once_t的變量是一個控制變量。控制變量必須使用PTHREAD_ONCE_INIT宏靜態(tài)地初始化。例7.4pthread_once函數(shù)的應用(once.c)#include<pthread.h>pthread_once_tonce=PTHREAD_ONCE_INIT;pthread_mutex_t
mutex;/*互斥量*/voidonce_init_routine(void)
/*一次初始化函數(shù)*/{intstatus;status=pthread_mutex_init(&mutex,NULL);/*初始化互斥量*/
If(status==0)
printf(“Init
success!,Myidis%u”,pthread_self());}void*child_thread(void*arg){printf(“I’mchild,Myidis%u”,pthread_self());
pthread_once(&once,once_init_routine);/*子線程調用一次性初始化函數(shù)*/}int
main(int
argc,char*argv[]){pthread_t
child_thread_id;pthread_create(&child_thread_id,NULL,child_thread,NULL);/*創(chuàng)建子線程*/
printf(“I’m
father,myidis%u”,pthread_self());
pthread_once(&once_block,once_init_routine);/*父線程調用一次性初始化函數(shù)*/
pthread_join(child_thread_id,NULL);}程序運行結果如下:./onceI’mfather,Myidis3086874304Initsuccess!,Myidis3086874304I’mchild,Myidis3086871472 從上面的結果可以看到當主函數(shù)初始化成功后,子函數(shù)初始化失敗。7.1.5線程的私有數(shù)據(jù)在進程內的所有線程共享相同的地址空間,任何聲明為靜態(tài)或外部的變量,或在進程堆聲明的變量,都可以被進程所有的線程讀寫。但有時在應用程序設計過程中有必要提供線程私有的全局變量,僅在線程中有效,卻可跨多個函數(shù)進行訪問,如程序可能需要每個線程維護一個鏈表,要使用相同的函數(shù)操作,最簡單的辦法就是使用同名而不同變量地址的線程相關數(shù)據(jù)結構。這樣的數(shù)據(jù)結構可以由POSIX線程庫維護,稱為線程私有數(shù)據(jù)(Thread-SpecificData,TSD)。7.1.5線程的私有數(shù)據(jù)POSIX線程庫提供了函數(shù)pthread_key_create來創(chuàng)建線程鍵。其函數(shù)原型為:#include<pthread.h>int
pthread_key_create(pthread_key*key,void(*destructor)(void*));函數(shù)參數(shù):參數(shù)key為私有數(shù)據(jù)鍵,destructor為清理函數(shù)。返回值:若函數(shù)調用成功返回0,否則返回錯誤編號。函數(shù)說明:函數(shù)用于建立線程私有數(shù)據(jù)鍵。函數(shù)的第1個參數(shù)為指向一個鍵值的指針,第2個參數(shù)指明了一個destructor函數(shù)(清理函數(shù)),如果這個參數(shù)不為空,那么當每個線程結束時,系統(tǒng)將調用這個函數(shù)來釋放綁定在這個鍵上的內存塊。這個函數(shù)常和函數(shù)pthread_once一起使用,為了讓這個鍵只被創(chuàng)建一次。函數(shù)pthread_once聲明一個初始化函數(shù),第一次調用pthread_once時它執(zhí)行這個函數(shù),以后的調用將被它忽略。
例7.5pthread_key_create函數(shù)的應用#include<pthread.h>pthread_key_t
tsd_key;pthread_once_t
key_once=PTHREAD_ONCE_INIT;voidonce_routine(void){intstatus;status=pthread_key_create(&tsd_key,NULL);/*初始化線程私有數(shù)據(jù)鍵*/if(status=0)printf(“Keycreatesuccess!Myidis%u\n”,pthread_self());}void*child_thread(void*arg){printf(“I’m
child,Myidis%u\n”,pthread_self());
pthread_once(&key_once,once_routine);/*調用一次性初始化函數(shù)*/}int
main(int
argc,char*argv[]){pthread_t
child_thread_id;pthread_create(&child_thread_id,NULL,child_thread,NULL);printf(“I’m
father,myidis%u\n”,pthread_self());pthread_once(&key_once,once_routine);}程序運行結果如下:I’mfather,Myidis3086231232Keycreatesuccess!Myidis3086231232I’mchild,Myidis20862284007.2信號燈7.2.1
POSIX有名信號燈的API函數(shù)有名信號燈總是既可用于線程間的同步,又可以用于進程間的同步。1.sem_open函數(shù)sem_open函數(shù)用于創(chuàng)建一個新的有名信號燈或打開一個已存在的有名信號燈。函數(shù)原型如下:#include<semaphore.h>sem_t*sem_open(constchar*name,int
oflag,/*mode_t
mode,unsigned
intvalue*/);函數(shù)參數(shù):參數(shù)name是信號燈外部名字,oflag為選擇創(chuàng)建或打開一個現(xiàn)有的信號燈,mode是權限位,value是信號燈初值。返回值:若函數(shù)調用成功時返回指向信號燈的指針,出錯時為SEM_FAILED。函數(shù)說明:oflag參數(shù)可以是0、O_CREAT(創(chuàng)建一個信號燈)或O_CREAT|O_EXCL(如果沒有指定的信號燈就創(chuàng)建),如果指定了O_CREAT,那么第3個和第4個參數(shù)是需要的;其中mode參數(shù)指定權限位,value參數(shù)指定信號燈的初始值,通常用來指定共享資源的頁面。該初始不能超過SEM_VALUE_MAX,這個常值必須低于為32767。二值信號燈的初始值通常為1,計數(shù)信號燈的初始值則往往大于1。
如果指定了O_CREAT(而沒有指定O_EXCL),那么只有所需的信號燈尚未存在時才初始化它。所需信號燈已存在條件下指定O_CREAT不是一個錯誤。該標志的意思僅僅是“如果所需信號燈尚未存在,那就創(chuàng)建并初始化它”。但是所需信號燈等已存在條件下指定O_CREAT|O_EXCL卻是一個錯誤。
sem_open返回指向sem_t信號燈的指針,該結構里記錄著當前共享資源的數(shù)目。2.sem_close函數(shù)用于關閉有名信號燈。函數(shù)原型如下:#include<semaphore.h>int
sem_close(sem_t*sem);函數(shù)參數(shù):參數(shù)sem是指向信號燈的指針。返回值:若函數(shù)調用成功則返回0,否則返回-1。函數(shù)說明:一個進程終止時,內核還對其上仍然打開著的所有有名信號燈自動執(zhí)行這樣的信號燈關閉操作。不論該進程是自愿終止的還是非自愿終止的,這種自動關閉都會發(fā)生。
但應注意的是關閉一個信號燈并沒有將它從系統(tǒng)中刪除。即Posix有名信號燈至少是隨內核持續(xù)的:即使當前沒有進程打開著某個信號燈,它的值仍然保持。
3.sem_unlink函數(shù)sem_unlink函數(shù)用于在所有進程關閉了命名信號量之后,將信號量從系統(tǒng)中刪除。函數(shù)原型如下:#include<semaphore.h>int
sem_unlink(countchar*name);函數(shù)參數(shù):參數(shù)name是信號燈的外部名字。返回值:若函數(shù)調用成功則返回0,否則返回-1。函數(shù)說明:有名信號燈使用sem_unlink從系統(tǒng)中刪除。每個信號燈有一個引用計數(shù)器記錄當前的打開次數(shù),sem_unlink必須等待這個數(shù)為0時才能把name所指的信號燈從文件系統(tǒng)中刪除。也就是要等待最后一個sem_close發(fā)生。
4.sem_getvalue函數(shù)函數(shù)sem_getvalue用于測試信號燈。函數(shù)原型如下:#include<semaphore.h>int
sem_getvalue(sem_t*sem,int*valp);函數(shù)參數(shù):參數(shù)sem是指向信號燈的指針。返回值:若函數(shù)調用成功則返回0,否則返回-1。函數(shù)說明:sem_getvalue在由valp指向的正數(shù)中返回所指定信號燈的當前值。如果該信號燈當前已上鎖,那么返回值或為0,或為某個負數(shù),其絕對值就是等待該信號燈解鎖的線程數(shù)。5.sem_wait/sem_trywait函數(shù)函數(shù)用于等待共享資源。函數(shù)原型如下:#include<semaphore.h>int
sem_wait(sem_t*sem);int
sem_trywait(sem_t*sem);函數(shù)參數(shù):參數(shù)sem是指向信號燈的指針。返回值:若函數(shù)調用成功則返回0,否則返回-1。函數(shù)說明:可以用sem_wait來申請共享資源,sem_wait函數(shù)可以測試所指定信號燈的值,如果該值大于0,那就將它減1并立即返回??梢允褂蒙暾垇淼墓蚕碣Y源。如果該值等于0,調用線程就被進入睡眠狀態(tài),直到該值變?yōu)榇笥?,這時再將它減1,函數(shù)隨后返回。sem_wait操作必須是原子的。sem_wait和sem_trywait的差別是:當所指定信號燈的值已經(jīng)是0時,后者并不將調用線程投入睡眠。相反,它返回一個EAGAIN錯誤。6.sem_post函數(shù)函數(shù)sem_post(sem_t*sem)用來增加信號量的值。當有線程阻塞在這個信號量上時,調用這個函數(shù)會使其中的一個線程不在阻塞,函數(shù)原型如下:#include<semaphore.h>int
sem_post(sem_t*sem);int
sem_getvalue(sem_t*sem,int*valp);函數(shù)參數(shù):參數(shù)sem是指向信號燈的指針。返回值:若函數(shù)調用成功則返回0,否則返回-1。函數(shù)說明:當一個線程使用完某個信號燈時,它應該調用sem_post來告訴系統(tǒng)申請的資源已經(jīng)用完。本函數(shù)和sem_wait函數(shù)功能正好相反,它把所指定的信號燈的值加1,然后喚醒正在等待該信號燈值變?yōu)檎龜?shù)的任意線程。posix有名信號燈使用時應注意以下幾點:(1)Posix有名信號燈的值是隨內核持續(xù)的。也就是說,一個進程創(chuàng)建了一個信號燈,這個進程結束后,這個信號燈還存在,并且信號燈的值也不會改變。(2)當持有某個信號燈鎖的進程沒有釋放它就終止時,內核并不給該信號燈解鎖。例7.6posix有名信號燈應用于多線程(7-6.c)#include<semaphore.h>#include<unistd.h>#include<stdio.h>#include<fcntl.h>#include<thread.h>void*thread_function(void*arg);/*線程入口函數(shù)*/voidprint(pid_t);/*共享資源函數(shù)*/sem_t*sem;/*定義Posix有名信號燈*/int
val;/*定義信號燈當前值*/int
main(int
argc,char*argv[]){intn=0;if(argc!=2){printf(“pleaseinputafilename!\n”);exit(1);}sem=sem_open(argv[1],O_CREAT,0644,3);/*打開一個信號燈*/while(n++<5)/*循環(huán)創(chuàng)建5個子線程,使它們同步運行*/{if((pthread_create(&a_thread,NULL,
thread_function,NULL))!=0)
{perror(“Threadcreationfailed”);exit(1);}}pthread_join(a_thread,NULL);sem_close(bin_sem);sem_unlink(argv[1]);}void*thread_function(void*arg){sem_wait(sem);/*申請信號燈*/print();/*調用共享代碼段*/sleep(1);
sem_post(sem);/*釋放信號燈*/
printf(“I’m
finished,my
tidis%d\n”,pthread_self());}voidprint(){printf(“Igetit,my
tidis%d\n”,pthread_self());sem_getvalue(sem,&val);printf(“Nowthevaluehave%d\n”,val);}
程序用循環(huán)的方法建立5個線程,然后讓它們調用同一個線程處理函數(shù)thread_function,在函數(shù)里利用信號量來限制訪問共享資源的線程數(shù)。共享資源用print函數(shù)來代表,在真正編程中它有可以是一個終端設備(如打印機)或是一段有實際意義的代碼。運行結果為:#gcc–lpthread–o7_6.c#./7_6testIgetit,my
tidis1082330304Nowthevaluehave2Igetit,my
pidis1894Nowthevaluehave1Igetit,my
pidis1895Nowthevaluehave0I’mfinished,my
pidis1893I’mfinished,my
pidis1894I’mfinished,my
pidis1895Igetit,my
pidis1896Nowthevaluehave2Igetit,mypidis1897Nowthevaluehave1I’mfinished,my
pidis1896I’mfinished,my
pidis1897例7.7posix有名信號燈應用于多進程(7-7.c)下面是應用Posix有名信號燈的一個小程序。用它來限制訪問共享代碼的進程數(shù)目。#include<semaphore.h>#include<unistd.h>#include<stdio.h>#include<fcntl.h>voidprint(pid_t);sem_t*sem;/*定義Posix有名信號燈*/int
val;/*定義信號燈當前值*/int
main(int
argc,char*argv[]){intn=0;if(argc!=2){printf(“pleaseinputafilename!\n”);exit(1);}sem=sem_open(argv[1],O_CREAT,0644,2);/*打開一個信號燈,初值設為2*/while(n++<5)/*循環(huán)創(chuàng)建5個子進程,使它們同步運行*/{if(fork()==0){sem_wait(sem);/*申請信號燈*/
print(getpid());/*調用共享代碼段*/sleep(1);
sem_post(sem);/*釋放信號燈*/
printf(“I’m
finished,my
pidis%d\n”,getpid());
return0;}wait();/*等待所有子進程結束*/sem_close(sem);sem_unlink(argv[1]);exit(0);}voidprint(pid_t
pid){printf(“Igetit,my
pidis%d\n”,pid);
sem_getvalue(sem,&val);
printf(“Nowthevaluehave%d\n”,val);}程序編譯后運行會得到如下結果:#./7_7testIgetit,my
tidis1082330304Nowthevaluehave1Igetit,my
tidis1090718784Nowthevaluehave0Ifinished,my
pidis1082330304Ifinished,my
pidis1090718784Igetit,my
tidis1099107264Nowthevaluehave1Igetit,my
tidis1116841120Nowthevaluehave0Ifinished,my
pidis1099107264Ifinished,my
pidis1116841120Igetit,my
tidis1125329600Nowthevaluehave1Ifinished,my
pidis1125329600
7.2.2
POSIX基于內存的信號燈的API函數(shù)1.sem_init/sem_destroy/sem_getvalue
函數(shù)
函數(shù)sem_init/sem_destroy/sem_getvalue用于初始化/關閉信號/獲取信號燈的值等。函數(shù)原型如下:#include<semaphore.h>int
sem_init(sem_t*sem,int
shared,unsigned
intvalue);int
sem_getvalue(sem_t*sem);/*獲取信號燈的值*/函數(shù)參數(shù):參數(shù)sem為指向信號燈的指針,shared是作用范圍,value是信號燈初始值。返回值:若函數(shù)調用成功則返回0,否則返回-1。函數(shù)說明:基于內存的信號燈是由sem_init初始化的。sem參數(shù)指向必須由應用程序分配的sem_t變量。如果shared為0,那么待初始化的信號燈是在同一進程的各個線程共享的,否則該信號燈是在進程間共享的。當shared為零時,該信號燈必須存放在即將使用它的所有進程都能訪問的某種類型的共享內存中。跟sem_open一樣,value參數(shù)是該信號燈的初始值。使用完一個基于內存的信號燈后,可調用sem_destroy關閉它。除了sem_open和sem_close外,其它的poisx有名信號燈函數(shù)都可以用于基于內存的信號燈。注意:posix基于內存的信號燈和posix有名信號燈的區(qū)別,區(qū)別如下。(1)sem_open不需類型與shared的參數(shù),有名信號燈總是可在不同進程間共享的。(2)sem_init不使用任何類似于O_CREAT標志的東西,也就是說,sem_init總是初始化信號燈的值。因此,對于一個給定的信號燈,必須小心保證只調用一次sem_init。(3)sem_open返回一個指向某個sem_t變量的指針,該變量由函數(shù)本身分配并初始化。但sem_init的第一個參數(shù)是一個指向某個sem_t變量的指針,該變量由調用者分配,然后由sem_init函數(shù)初始化。(4)posix有名信號燈是通過內核持續(xù)的,一個進程創(chuàng)建一個信號燈,另外的進程可以通過該信號燈的外部名(創(chuàng)建信號燈使用的文件名)來訪問它。例7.8下面是posix基于內存的信號燈實現(xiàn)一個進程的各個線程間的互次。#include<semaphore.h>#include<unistd.h>#include<stdio.h>#include<fcntl.h>#include<pthread.h>#incude<stdlib.h>void*thread_function(void*arg);/*線程入口函數(shù)*/voidprint(void);/*共享資源*/sem_t
bin_sem;/*定義信號燈*/intvalue;/*定義信號量的
intmain(){intn=0;pthread_t
a_thread;if((sem_init(&bin_sem,0,2))!=0)/*初始化信號燈,初始值為2*/{perror(“sem_init”);exit(1);}while(n++<5)/*循環(huán)創(chuàng)建5個線程*/{if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0){perror(“Threadcreationfailed”);exit(1);}}pthread_join(a_thread,NULL);/*等待子線程返回*/}void*thread_function(void*arg){sem_wait(&bin_sem);/*等待信號燈*/print();sleep(1);sem_post(&bin_sem);/*掛出信號燈*/printf(“I
finished,my
pidis%d\n”,pthread_self());pthread_exit(arg);}voidprint(){printf(“Igetit,my
tidis%d\n”,pthread_self());sem_getvalue(&bin_sem,&value);/*獲取信號燈的值*/printf(“Nowthevaluehave%d\n”,value);}下面是運行結果:#gcc–lpthread–oseminitthread
seminitthread.c#./seminitthread
Igetit,my
tidis1082330304Nowthevaluehave1Igetit,my
tidis1090718784Nowthevaluehave0Ifinished,my
pidis1082330304Ifinished,my
pidis1090718784Igetit,my
tidis1099107264Nowthevaluehave1Igetit,my
tidis1116841120Nowthevaluehave0Ifinished,my
pidis1099107264Ifinished,my
pidis1116841120Igetit,my
tidis1125329600Nowthevaluehave1Ifinished,my
pidis1125329600//下面是用exit()函數(shù)結束進程的程序代碼:#include<stdlib.h>#include<stdio.h>int
main(void){printf("Usingexit...\n");
printf("Thisisthecontentinbuffer");
exit(0);}輸出信息:Usingexit...Thisisthecontentinbuffer7.3互斥量1.mutex線程訪問控制(1)初始化互斥量用pthread_mutex_t數(shù)據(jù)類型來表示,在使用互斥量之前,必須首先對它進行初始化,可以把它置為常量PTHREAD_MUTEX_INITIALIZER(只對靜態(tài)分配的互斥量),也可以通過調用pthread_mutex_init函數(shù)進行初始化,如果動態(tài)地分配互斥量,那么釋放內存前需要調用pthread_mutex_destroy。下面介紹常用的互斥量函數(shù)。pthread_mutex_init函數(shù)用于初始化互斥鎖。用于初始化互斥鎖。其函數(shù)#include<pthread.h>int
pthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutex_t*attr);//初始化互斥鎖int
pthread_mutex_destroy(pthread_mutex_t*mutex);//銷毀互斥鎖函數(shù)參數(shù):參數(shù)mutex是互斥量,attr為互斥鎖屬性。返回值:若函數(shù)調用成功則返回0,否則返回錯誤編號。。函數(shù)說明:mutex為要鎖住的互斥量,attr是互斥鎖的屬性。如果要使用默認的屬性初始化互斥量,只需把attr設置為NULL。(2)互斥量加鎖函數(shù)對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經(jīng)上了鎖,調用線程會阻塞,直到互斥量被解鎖。在完成了對共享資源的訪問后,要對互斥量進行解鎖。互斥量加鎖函數(shù)原型如下:#include<pthread.h>int
pthread_mutex_lock(pthread_mutex_t*mutex);int
pthread_mutex_trylock(pthread_mutex_t*mutex);函數(shù)參數(shù):參數(shù)mutex是互斥量返回值:成功則返回0,出錯則返回錯誤編號函數(shù)說明:trylock函數(shù),這個函數(shù)是非阻塞調用模式,也就是說,如果互斥量沒被鎖住,trylock函數(shù)將把互斥量加鎖,并獲得對共享資源的訪問權限;如果互斥量被鎖住了,trylock函數(shù)將不會阻塞等待而直接返回EBUSY,表示共享資源處于忙狀態(tài)。(3)互斥量解鎖函數(shù)函數(shù)原型:#include<pthread.h>int
pthread_mutex_unlock(pthread_mutex_t*mutex);函數(shù)參數(shù):參數(shù)mutex是互斥量返回值:成功則返回0,出錯則返回錯誤編號對互斥量進行加鎖,需要調用pthread_mutex_lock,如果互斥量已經(jīng)上鎖,調用線程阻塞直至互斥量解鎖。對互斥量解鎖,需要調用pthread_mutex_unlock.
如果線程不希望被阻塞,可以使用pthread_mutex_trylock嘗試對互斥量進行加鎖。如果調用pthread_mutex_trylock時互斥量處于未鎖住狀態(tài),那么pthread_mutex_trylock將鎖住互斥量,否則就會失敗,不能鎖住互斥量,而返回EBUSY。例
7.9對互斥量加鎖#inlcude<stdio.h>#include<pthread.h>#inlcude<stdio.h>#include<unistd.h>void*thread_function(void*arg);int
run_now=1;/*用run_now代表共享資源*/intmain(){intprint_count1=0;/*用于控制循環(huán)*/pthread_t
a_thread;if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0)/*創(chuàng)建一個線程*/{
perror(“Thread
createionfailed”);exit(1);}while(print_count1++<5){if(run_now==1)/*主線程:如果run_now為1就把它修改為2*/{printf(“mainthreadisrun\n”);
run_now=2;}else{printf(“mainthreadissleep\n”);sleep(1);}}pthread_join(a_thread,NULL);/*等待子線程結束*/exit(0);}void*thread_function(void*arg){intprint_count2=0;while(print_count2++<5){
if(run_now==2)/子線程:如果run_now為2就把它修改為1*/{printf(“functionthreadisrun\n”);
run_now=1;}else{
printf(“functionthreadissleep\n”);sleep(1);}}pthread_exit(NULL);}運行上面程序的運行結果為:functionthreadissleepmainthreadisrunmainthreadissleepmainthreadissleepfunctionthreadisrunfunctionthreadissleepmainthreadisrunmainthreadissleepfunctionthreadisrunfunctionthreadissleep可以看到main線程和function線程是交替運行的。它們都可以對run_now進行操作。例7.10下面是加鎖的程序。#inlcude<stdio.h>#include<pthread.h>#inlcude<stdio.h>viid*thread_function(void*arg);int
run_now=1;/*用run_now代表共享資源*/pthread_mutex_t
work_mutex;/*定義互斥量*/intmain(){intres;intprint_count1=0;prhread_t
a_thread;if(pthread_mutex_init(&work_mutex,NULL)!=0)/*初始化互斥量*/{perror(“Mutexinitfaied”);exit(1);}if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0)/*創(chuàng)建新線程*/{perror(“Thread
createionfailed”);exit(1);}if(pthread_mutex_lock(&work_mutex)!=0)/*對互斥量加鎖*/{
preeor(“Lockfailed”);exit(1);}else
printf(“mainlock\n”);while(print_count1++<5){if(run_now==1)/主線程:如果run_now為1就把它修改為2*/{printf(“mainthreadisrun\n”);
run_now=2;}else{printf(“mainthreadissleep\n”);sleep(1);}}if(pthread_mutex_unlock(&work_mutex)!=0)/*對互斥量解鎖*/{preeor(“unlockfailed”);exit(1);}else
printf(“mainunlock\n”);pthread_mutex_destroy(&work_mutex);/*收回互斥量資源*/pthread_join(a_thread,NULL);/*等待子線程結束*/exit(0);}void*thread_function(void*arg){intprint_count2=0;sleep(1);if(pthread_mutex_lock(&work_mutex)!=0){perror(“Lockfailed”);exit(1);}else
printf(“functionlock\n”);while(print_count2++<5){if(run_now==2)/分進程:如果run_now為1就把它修改為1*/{printf(“functionthreadisrun\n”);
run_now=1;}else{printf(“functionthreadissleep\n”);sleep(1);}}if(pthread_mutex_unlock(&work_mutex)!=0)/*對互斥量解鎖*/{perror(“unlockfailed”);exit(1);}else
printf(“functionunlock\n”);pthread_exit(NULL);}下面是運行結果:mainlockmainthreadisrunmainthreadissleepmainthreadissleepmainthreadissleepmainthreadissleepmainunlockfunctionlockfunctionthreadisrunfunctionthreadissleepfunctionthreadissleepfunctionthreadissleepfunctionthreadissleepfunctionunlock2.互斥鎖屬性線程和線程的同步對象(互斥量,讀寫鎖,條件變量)都具有屬性。在修改屬性前都需要對該結構進行初始化。使用后要把該結構回收。用pthread_mutexattr_init函數(shù)對pthread_mutexattr結構進行初始化。用pthread_mutexattr_destroy函數(shù)對該結構進行回收。(1)pthread_mutexattr_init/pthread_mutexattr_destroy函數(shù)用于初始化/回收pthread_mutexattr_t結構。其函數(shù)原型如下:#include<pthread.h>int
pthread_mutexattrattr_init(pthread_mutexattr_t*attr);int
pthread_mutexattrattr_destroy(pthread_mutexattr_t*attr);函數(shù)參數(shù):pthread_mutexattr_t結構變量,attr為互斥鎖屬性。返回值:若函數(shù)調用成功則返回0,否則返回錯誤編號。。函數(shù)說明:pthread_mutexattr_init將屬性對象的值初始化為缺省值。并分配屬性對象占用的內存空間。參數(shù)attr中pshared屬性表示用這個屬性對象創(chuàng)建的互斥鎖的作用域,它的取值可以是PTHREAD_PROCESS_PRIVATE(缺省值該屬性對象創(chuàng)建的互斥鎖只能在進程內使用)或PTHREAD_PROCESS_SHARED。(2)pthread_mutexattr_getpshared/pthread_mutexattr_setpshared函數(shù)用于獲得/修改共享互斥量屬性。其函數(shù)原型如下:
#include<pthread.h>
int
pthread_mutexattrattr_getpshared(constpthread_attr_t*restrictattr,int*restrictpshared);
int
pthread_mutexattrattr_setpshared(constpthread_attr_t*restrictattr,int
pshared);
返回值:若函數(shù)調用成功則返回0,否則返回錯誤編號。函數(shù)說明:互斥量屬性分為共享互斥量屬性和類型互斥量屬性。兩種屬性分別由不同的函數(shù)得到并由不同的函數(shù)進行修改。pthread_mutexattr_getpshared/pthread_mutexattr_setpshared函數(shù)可以獲得和修改共享互斥量屬性。pthread_mutexattr_gettype和pthread_mutexattr_settype函數(shù)可以獲得和修改類型互斥量屬性。
共享互斥量屬性用于規(guī)定互斥鎖的作用域?;コ怄i的域可以是進程內的也可以是進程間的。pthread_mutexattrattr_getpshared可以返回屬性對象的互斥鎖作用域屬性。可是以下值:PTHREAD_PROCESS_SHARED,PTHREAD_PROCESS_PRIVATE。如果互斥鎖屬性對象的pshared屬性被置PTHREAD_PROCESS_SHARED。那么由這個屬性對象創(chuàng)建的互斥鎖將被保存在共享內存中,可以被多個進程中的線程共享。如果pshared屬性被置為PTHREAD_PROCESS_PRIVATE,那么只有和創(chuàng)建這個互斥鎖的線程在同一個進程中的線程才能訪問這個互斥鎖。
(3)pthread_mutexattr_gettype/pthread_mutexattr_settype
函數(shù)用于獲得/修改類型互斥量屬性。其函數(shù)原型如下:
#include<pthread.h>
int
pthread_mutexattr_gettype(pthread_mutexattr_t*attr,int*type);
int
pthread_mutexattr_settype(pthread_mutexattr_t*attr,inttype);返回值:若函數(shù)調用成功則返回0,否則返回錯誤編號。。函數(shù)說明:pthread_mutexattr_gettype函數(shù)可以獲得互斥鎖類型屬性,pthread_mutexattr_settype函數(shù)可用來設置互斥鎖的type屬性。缺省的互斥鎖類型屬性是PTHREAD_MUTEX_DEFAULT。合法的類型屬性值有:PTHREAD_MUTEX_NORMAL;PTHREAD_MUTEX_ERRORCHECK;PTHREAD_MUTEX_RECURSIVE;PTHREAD_MUTEX_DEFAULT。類型說明:①PTHREAD_MUTEX_NORMAL這種類型的互斥鎖不會自動檢測死鎖。如果一個線程試圖對一個互斥鎖重復鎖定,將會引起這個線程的死鎖。如果試圖解鎖一個由別的線程鎖定的互斥鎖會引發(fā)不可預料的結果。如果一個線程試圖解鎖已經(jīng)被解鎖的互斥鎖也會引發(fā)不可預料的結果。②PTHREAD_MUTEX_ERRORCHECK這種類型的互斥鎖會自動檢測死鎖。若一個線程試圖對一個互斥鎖重復鎖定,則返回錯誤代碼。若試圖解鎖一個由別的線程鎖定的互斥鎖,則返回錯誤代碼。若一個線程試圖解鎖已經(jīng)被解鎖的互斥鎖,則返回錯誤代碼。③PTHREAD_MUTEX_RECURSIVE一個線程對這種類型的互斥鎖重復上鎖,不會引起死鎖,一個線程對這類互斥鎖的多次重復上鎖必須由這個線程來重復相同數(shù)量的解鎖,否則別的線程不能得到這個互斥鎖。試圖解鎖一個由別的線程鎖定的互斥鎖將會返回錯誤代碼。一個線程試圖解鎖已經(jīng)被解鎖的互斥鎖也將會返回錯誤代碼。這種類型的互斥鎖只能是進程私有的(作用域屬性為THREAD_PROCESS_PRIVATE)。④PTHREAD_MUTEX_DEFAULT
這種類型的互斥鎖不會自動檢測死鎖。如果一個線程試圖對一個互斥鎖重復鎖定,將會引起不可預料的結果。如果試圖解鎖一個由別的線程鎖定的互斥鎖會引發(fā)不可預料的結果。如果一個線程試圖解鎖已經(jīng)被解鎖的互斥鎖也會引發(fā)不可預料的結果。POSIX標準規(guī)定,對于某一具體的實現(xiàn),可以把這種類型的互斥鎖定義為其他類型的互斥鎖。應用互斥量需要注意的幾點:(1)互斥量需要時間來加鎖和解鎖。鎖住較少互斥量的程序通常運行得更快。所以,互斥量應該盡量少,夠用即可,每個互斥量保護的區(qū)域應則盡量大。(2)互斥量的本質是串行執(zhí)行。如果很多線程需要領繁地加鎖同一個互斥量,則線程的大部分時間就會在等待,這對性能是有害的。如果互斥量保護的數(shù)據(jù)(或代碼)包含彼此無關的片段,則可以特大的互斥量分解為幾個小的互斥量來提高性能。這樣,任意時刻需要小互斥量的線程減少,線程等待時間就會減少。所以,互斥量應該足夠多(到有意義的地步),每個互斥量保護的區(qū)域則應盡量的少。
7.4條件變量與互斥鎖不同,條件變量是用來等待而不是用來上鎖的。條件變量用來自動阻塞一個線程,直到某特殊情況發(fā)生為止。通常條件變量和互斥鎖同時使用。條件變量可以睡眠等待某種條件出現(xiàn)。條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使“條件成立”(給出條件成立信號)。條件的檢測是在互斥鎖的保護下進行的。如果一個條件為假,一個線程自動阻塞,并釋放等待狀態(tài)改變的互斥鎖。如果另一個線程改變了條件,它發(fā)信號給關聯(lián)的條件變量,喚醒一個或多個等待它的線程,重新獲得互斥鎖,重新評價條件。如果兩進程共享可讀寫的內存,條件變量可以被用來實現(xiàn)這兩進程間的線程。1.初始化條件變量屬性使用pthread_condattr_init函數(shù)可以將與該對象相關聯(lián)的屬性初始化為其缺省值。在執(zhí)行過程中,線程系統(tǒng)會為每個屬性對象分配存儲空間。其函數(shù)原型如下:#include<pthread.h>int
pthread_condattr_init(pthread_condattr_t*attr);返回值:若函數(shù)調用成功則返回0,否則返回錯誤編號,錯誤碼為ENOMEM,表示分配的內存不足,無法初始化線程屬性對象;錯誤碼為EINVAL表示attr
指定的值無效。函數(shù)說明:函數(shù)調用時,pshared
屬性的缺省值為PTHREAD_PROCESS_PRIVATE,表示可以在進程內使用已初始化的條件變量。attr
的數(shù)據(jù)類型為pthread_condattr_t,其中包含一個由系統(tǒng)分配的屬性對象。attr
范圍可能的值為PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。2.刪除條件變量屬性使用pthread_condattr_destroy函數(shù)可以刪除存儲并使屬性對象無效。其函數(shù)原型如下:#include<pthread.h>int
pthread_condattr_destroy(pthread_condattr_t*attr);返回值:若函數(shù)調用成功則返回0,否則返回錯誤編號。函數(shù)說明:條件變量屬性必須首先由pthread_condattr_destroy函數(shù)刪除條件變量屬性后,重新初始化后才能重用。3.設置條件變量的范圍pthread_condattr_setpshared函數(shù)可用來將條件變量的范圍設置為進程專用(進程內)或系統(tǒng)范圍內(進程間)。其函數(shù)原型如下:#include<pthread.h>Int
pthread_condattr_setpshared(pthread_condattr_t*cattr,int
pshared);返回值:若函數(shù)調用成功則返回0,否則返回錯誤編號。函數(shù)說明:條件變量屬性必須首先由pthread_condattr_destroy函數(shù)刪除條件變量屬性重新初始化后才能重用。例如:ret=pthread_condattr_setpshared(&cattr,PTHREAD_PROCESS_SHARED);或ret=pthread_condattr_setpshared(&cattr,PTHREAD_PROCESS_PRIVATE);如果pshared
屬性在共享內存中設置為PTHREAD_PROCESS_SHARED,則其所創(chuàng)建的條件變量可以在多個進程中的線程之間共享。此行為與最初的Solaris線程實現(xiàn)中mutex_init()中的USYNC_PROCESS標志等效。如果互斥鎖的pshared
屬性設置為PTHREAD_PROCESS_PRIVATE,則僅有那些由同一個進程創(chuàng)建的線程才能夠處理該互斥鎖。PTHREAD_PROCESS_PRIVATE是缺省值。PTHREAD_PROCESS_PRIVATE所產(chǎn)生的行為與在最初的Solaris線程的cond_init()調用中使用USYNC_THREAD標志相同。PTHREAD_PROCESS_PRIVATE的行為與局部條件變量相同。
4.獲取條件變量的范圍pthread_condattr_getpshared函數(shù)可用來獲取屬性對象cattr
的pshared
的當前值。其函數(shù)原型如下:#include<pthread.h>int
pthread_condattr_getpshared(const
pthread_condattr_t*cattr,in
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 手術準備與操作規(guī)范管理制度
- 手術環(huán)境管理制度
- 2022年三年級語文下冊第四單元主題閱讀+答題技巧(含答案、解析)部編版
- 2024年客運證考什么的
- 2024年嘉峪關小型客運從業(yè)資格證考試題答案
- 2024年宜春客運從業(yè)資格證模擬考試練習題
- 2024年道路客運從業(yè)資格證繼續(xù)教育模擬考試
- 2024年綿陽a1客運資格證
- 2024年??诳瓦\從業(yè)資格證的考試題目
- 2024年河北客運上崗考試都考什么科目
- 土地違法行為及法律責任
- 供應商響應情況登記表
- 內鏡室醫(yī)療質量評價體系與考核標準
- 特異體質學生登記表( 小學)
- 機械工程控制基礎課后習題答案
- jgj113-2015建筑玻璃技術規(guī)范
- 金剛薩埵《百字明咒》梵文拼音標注
- 意識形態(tài)工作責任制落實情況專題匯報
- 《珍愛生命》主題班會
- 四川阿壩汶川縣機關事業(yè)單位選(考)調工作人員45人55筆試參考題庫答案解析版
- 社區(qū)矯正人員心得體會
評論
0/150
提交評論