線程編程指南(免費(fèi)).doc_第1頁
線程編程指南(免費(fèi)).doc_第2頁
線程編程指南(免費(fèi)).doc_第3頁
線程編程指南(免費(fèi)).doc_第4頁
線程編程指南(免費(fèi)).doc_第5頁
已閱讀5頁,還剩15頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Posix線程編程指南(1)內(nèi)容:一、 線程創(chuàng)建二、線程取消關(guān)于作者線程創(chuàng)建與取消楊沙洲()2001 年 10 月這是一個(gè)關(guān)于Posix線程編程的專欄。作者在闡明概念的基礎(chǔ)上,將向您詳細(xì)講述Posix線程庫API。本文是第一篇將向您講述線程的創(chuàng)建與取消。一、 線程創(chuàng)建11 線程與進(jìn)程相對(duì)進(jìn)程而言,線程是一個(gè)更加接近于執(zhí)行體的概念,它可以與同進(jìn)程中的其他線程共享數(shù)據(jù),但擁有自己的??臻g,擁有獨(dú)立的執(zhí)行序列。在串行程序基礎(chǔ)上引入線程和進(jìn)程是為了提高程序的并發(fā)度,從而提高程序運(yùn)行效率和響應(yīng)時(shí)間。線程和進(jìn)程在使用上各有優(yōu)缺點(diǎn):線程執(zhí)行開銷小,但不利于資源的管理和保護(hù);而進(jìn)程正相反。同時(shí),線程適合于在SMP機(jī)器上運(yùn)行,而進(jìn)程則可以跨機(jī)器遷移。12 創(chuàng)建線程POSIX通過pthread_create()函數(shù)創(chuàng)建線程,API定義如下:int pthread_create(pthread_t * thread, pthread_attr_t * attr,void * (*start_routine)(void *), void * arg)與fork()調(diào)用創(chuàng)建一個(gè)進(jìn)程的方法不同,pthread_create()創(chuàng)建的線程并不具備與主線程(即調(diào)用pthread_create()的線程)同樣的執(zhí)行序列,而是使其運(yùn)行start_routine(arg)函數(shù)。thread返回創(chuàng)建的線程ID,而attr是創(chuàng)建線程時(shí)設(shè)置的線程屬性(見下)。pthread_create()的返回值表示線程創(chuàng)建是否成功。盡管arg是void *類型的變量,但它同樣可以作為任意類型的參數(shù)傳給start_routine()函數(shù);同時(shí),start_routine()可以返回一個(gè)void *類型的返回值,而這個(gè)返回值也可以是其他類型,并由pthread_join()獲取。13 線程創(chuàng)建屬性pthread_create()中的attr參數(shù)是一個(gè)結(jié)構(gòu)指針,結(jié)構(gòu)中的元素分別對(duì)應(yīng)著新線程的運(yùn)行屬性,主要包括以下幾項(xiàng):_detachstate,表示新線程是否與進(jìn)程中其他線程脫離同步,如果置位則新線程不能用pthread_join()來同步,且在退出時(shí)自行釋放所占用的資源。缺省為PTHREAD_CREATE_JOINABLE狀態(tài)。這個(gè)屬性也可以在線程創(chuàng)建并運(yùn)行以后用pthread_detach()來設(shè)置,而一旦設(shè)置為PTHREAD_CREATE_DETACH狀態(tài)(不論是創(chuàng)建時(shí)設(shè)置還是運(yùn)行時(shí)設(shè)置)則不能再恢復(fù)到PTHREAD_CREATE_JOINABLE狀態(tài)。_schedpolicy,表示新線程的調(diào)度策略,主要包括SCHED_OTHER(正常、非實(shí)時(shí))、SCHED_RR(實(shí)時(shí)、輪轉(zhuǎn)法)和SCHED_FIFO(實(shí)時(shí)、先入先出)三種,缺省為SCHED_OTHER,后兩種調(diào)度策略僅對(duì)超級(jí)用戶有效。運(yùn)行時(shí)可以用過pthread_setschedparam()來改變。_schedparam,一個(gè)struct sched_param結(jié)構(gòu),目前僅有一個(gè)sched_priority整型變量表示線程的運(yùn)行優(yōu)先級(jí)。這個(gè)參數(shù)僅當(dāng)調(diào)度策略為實(shí)時(shí)(即SCHED_RR或SCHED_FIFO)時(shí)才有效,并可以在運(yùn)行時(shí)通過pthread_setschedparam()函數(shù)來改變,缺省為0。_inheritsched,有兩種值可供選擇:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新線程使用顯式指定調(diào)度策略和調(diào)度參數(shù)(即attr中的值),而后者表示繼承調(diào)用者線程的值。缺省為PTHREAD_EXPLICIT_SCHED。_scope,表示線程間競(jìng)爭(zhēng)CPU的范圍,也就是說線程優(yōu)先級(jí)的有效范圍。POSIX的標(biāo)準(zhǔn)中定義了兩個(gè)值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示與系統(tǒng)中所有線程一起競(jìng)爭(zhēng)CPU時(shí)間,后者表示僅與同進(jìn)程中的線程競(jìng)爭(zhēng)CPU。目前LinuxThreads僅實(shí)現(xiàn)了PTHREAD_SCOPE_SYSTEM一值。pthread_attr_t結(jié)構(gòu)中還有一些值,但不使用pthread_create()來設(shè)置。為了設(shè)置這些屬性,POSIX定義了一系列屬性設(shè)置函數(shù),包括pthread_attr_init()、pthread_attr_destroy()和與各個(gè)屬性相關(guān)的pthread_attr_get-/pthread_attr_set-函數(shù)。14 線程創(chuàng)建的Linux實(shí)現(xiàn)我們知道,Linux的線程實(shí)現(xiàn)是在核外進(jìn)行的,核內(nèi)提供的是創(chuàng)建進(jìn)程的接口do_fork()。內(nèi)核提供了兩個(gè)系統(tǒng)調(diào)用_clone()和fork(),最終都用不同的參數(shù)調(diào)用do_fork()核內(nèi)API。當(dāng)然,要想實(shí)現(xiàn)線程,沒有核心對(duì)多進(jìn)程(其實(shí)是輕量級(jí)進(jìn)程)共享數(shù)據(jù)段的支持是不行的,因此,do_fork()提供了很多參數(shù),包括CLONE_VM(共享內(nèi)存空間)、CLONE_FS(共享文件系統(tǒng)信息)、CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信號(hào)句柄表)和CLONE_PID(共享進(jìn)程ID,僅對(duì)核內(nèi)進(jìn)程,即0號(hào)進(jìn)程有效)。當(dāng)使用fork系統(tǒng)調(diào)用時(shí),內(nèi)核調(diào)用do_fork()不使用任何共享屬性,進(jìn)程擁有獨(dú)立的運(yùn)行環(huán)境,而使用pthread_create()來創(chuàng)建線程時(shí),則最終設(shè)置了所有這些屬性來調(diào)用_clone(),而這些參數(shù)又全部傳給核內(nèi)的do_fork(),從而創(chuàng)建的進(jìn)程擁有共享的運(yùn)行環(huán)境,只有棧是獨(dú)立的,由_clone()傳入。Linux線程在核內(nèi)是以輕量級(jí)進(jìn)程的形式存在的,擁有獨(dú)立的進(jìn)程表項(xiàng),而所有的創(chuàng)建、同步、刪除等操作都在核外pthread庫中進(jìn)行。pthread庫使用一個(gè)管理線程(_pthread_manager(),每個(gè)進(jìn)程獨(dú)立且唯一)來管理線程的創(chuàng)建和終止,為線程分配線程ID,發(fā)送線程相關(guān)的信號(hào)(比如Cancel),而主線程(pthread_create())的調(diào)用者則通過管道將請(qǐng)求信息傳給管理線程。二、線程取消21 線程取消的定義一般情況下,線程在其主體函數(shù)退出的時(shí)候會(huì)自動(dòng)終止,但同時(shí)也可以因?yàn)榻邮盏搅硪粋€(gè)線程發(fā)來的終止(取消)請(qǐng)求而強(qiáng)制終止。22 線程取消的語義線程取消的方法是向目標(biāo)線程發(fā)Cancel信號(hào),但如何處理Cancel信號(hào)則由目標(biāo)線程自己決定,或者忽略、或者立即終止、或者繼續(xù)運(yùn)行至Cancelation-point(取消點(diǎn)),由不同的Cancelation狀態(tài)決定。線程接收到CANCEL信號(hào)的缺省處理(即pthread_create()創(chuàng)建線程的缺省狀態(tài))是繼續(xù)運(yùn)行至取消點(diǎn),也就是說設(shè)置一個(gè)CANCELED狀態(tài),線程繼續(xù)運(yùn)行,只有運(yùn)行至Cancelation-point的時(shí)候才會(huì)退出。23 取消點(diǎn)根據(jù)POSIX標(biāo)準(zhǔn),pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函數(shù)以及read()、write()等會(huì)引起阻塞的系統(tǒng)調(diào)用都是Cancelation-point,而其他pthread函數(shù)都不會(huì)引起Cancelation動(dòng)作。但是pthread_cancel的手冊(cè)頁聲稱,由于LinuxThread庫與C庫結(jié)合得不好,因而目前C庫函數(shù)都不是Cancelation-point;但CANCEL信號(hào)會(huì)使線程從阻塞的系統(tǒng)調(diào)用中退出,并置EINTR錯(cuò)誤碼,因此可以在需要作為Cancelation-point的系統(tǒng)調(diào)用前后調(diào)用pthread_testcancel(),從而達(dá)到POSIX標(biāo)準(zhǔn)所要求的目標(biāo),即如下代碼段:pthread_testcancel();retcode = read(fd, buffer, length);pthread_testcancel();24 程序設(shè)計(jì)方面的考慮如果線程處于無限循環(huán)中,且循環(huán)體內(nèi)沒有執(zhí)行至取消點(diǎn)的必然路徑,則線程無法由外部其他線程的取消請(qǐng)求而終止。因此在這樣的循環(huán)體的必經(jīng)路徑上應(yīng)該加入pthread_testcancel()調(diào)用。25 與線程取消相關(guān)的pthread函數(shù)int pthread_cancel(pthread_t thread)發(fā)送終止信號(hào)給thread線程,如果成功則返回0,否則為非0值。發(fā)送成功并不意味著thread會(huì)終止。int pthread_setcancelstate(int state, int *oldstate)設(shè)置本線程對(duì)Cancel信號(hào)的反應(yīng),state有兩種值:PTHREAD_CANCEL_ENABLE(缺?。┖蚉THREAD_CANCEL_DISABLE,分別表示收到信號(hào)后設(shè)為CANCLED狀態(tài)和忽略CANCEL信號(hào)繼續(xù)運(yùn)行;old_state如果不為NULL則存入原來的Cancel狀態(tài)以便恢復(fù)。int pthread_setcanceltype(int type, int *oldtype)設(shè)置本線程取消動(dòng)作的執(zhí)行時(shí)機(jī),type由兩種取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,僅當(dāng)Cancel狀態(tài)為Enable時(shí)有效,分別表示收到信號(hào)后繼續(xù)運(yùn)行至下一個(gè)取消點(diǎn)再退出和立即執(zhí)行取消動(dòng)作(退出);oldtype如果不為NULL則存入運(yùn)來的取消動(dòng)作類型值。void pthread_testcancel(void)檢查本線程是否處于Canceld狀態(tài),如果是,則進(jìn)行取消動(dòng)作,否則直接返回。posix線程編程指南(2)內(nèi)容:一 概念及作用二 創(chuàng)建和注銷三 訪問四 使用范例關(guān)于作者相關(guān)內(nèi)容:(1) 線程創(chuàng)建與取消線程私有數(shù)據(jù)楊沙洲()2001 年 10 月這是一個(gè)關(guān)于Posix線程編程的專欄。作者在闡明概念的基礎(chǔ)上,將向您詳細(xì)講述Posix線程庫API。本文是第二篇將向您講述線程的私有數(shù)據(jù)。一 概念及作用在單線程程序中,我們經(jīng)常要用到全局變量以實(shí)現(xiàn)多個(gè)函數(shù)間共享數(shù)據(jù)。在多線程環(huán)境下,由于數(shù)據(jù)空間是共享的,因此全局變量也為所有線程所共有。但有時(shí)應(yīng)用程序設(shè)計(jì)中有必要提供線程私有的全局變量,僅在某個(gè)線程中有效,但卻可以跨多個(gè)函數(shù)訪問,比如程序可能需要每個(gè)線程維護(hù)一個(gè)鏈表,而使用相同的函數(shù)操作,最簡(jiǎn)單的辦法就是使用同名而不同變量地址的線程相關(guān)數(shù)據(jù)結(jié)構(gòu)。這樣的數(shù)據(jù)結(jié)構(gòu)可以由Posix線程庫維護(hù),稱為線程私有數(shù)據(jù)(Thread-specific Data,或TSD)。二 創(chuàng)建和注銷Posix定義了兩個(gè)API分別用來創(chuàng)建和注銷TSD:int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)該函數(shù)從TSD池中分配一項(xiàng),將其值賦給key供以后訪問使用。如果destr_function不為空,在線程退出(pthread_exit())時(shí)將以key所關(guān)聯(lián)的數(shù)據(jù)為參數(shù)調(diào)用destr_function(),以釋放分配的緩沖區(qū)。不論哪個(gè)線程調(diào)用pthread_key_create(),所創(chuàng)建的key都是所有線程可訪問的,但各個(gè)線程可根據(jù)自己的需要往key中填入不同的值,這就相當(dāng)于提供了一個(gè)同名而不同值的全局變量。在LinuxThreads的實(shí)現(xiàn)中,TSD池用一個(gè)結(jié)構(gòu)數(shù)組表示:static struct pthread_key_struct pthread_keysPTHREAD_KEYS_MAX = 0, NULL ;創(chuàng)建一個(gè)TSD就相當(dāng)于將結(jié)構(gòu)數(shù)組中的某一項(xiàng)設(shè)置為in_use,并將其索引返回給*key,然后設(shè)置destructor函數(shù)為destr_function。注銷一個(gè)TSD采用如下API:int pthread_key_delete(pthread_key_t key)這個(gè)函數(shù)并不檢查當(dāng)前是否有線程正使用該TSD,也不會(huì)調(diào)用清理函數(shù)(destr_function),而只是將TSD釋放以供下一次調(diào)用pthread_key_create()使用。在LinuxThreads中,它還會(huì)將與之相關(guān)的線程數(shù)據(jù)項(xiàng)設(shè)為NULL(見訪問)。三 訪問TSD的讀寫都通過專門的Posix Thread函數(shù)進(jìn)行,其API定義如下:int pthread_setspecific(pthread_key_t key, const void *pointer)void * pthread_getspecific(pthread_key_t key)寫入(pthread_setspecific())時(shí),將pointer的值(不是所指的內(nèi)容)與key相關(guān)聯(lián),而相應(yīng)的讀出函數(shù)則將與key相關(guān)聯(lián)的數(shù)據(jù)讀出來。數(shù)據(jù)類型都設(shè)為void *,因此可以指向任何類型的數(shù)據(jù)。在LinuxThreads中,使用了一個(gè)位于線程描述結(jié)構(gòu)(_pthread_descr_struct)中的二維void *指針數(shù)組來存放與key關(guān)聯(lián)的數(shù)據(jù),數(shù)組大小由以下幾個(gè)宏來說明: #define PTHREAD_KEY_2NDLEVEL_SIZE 32#define PTHREAD_KEY_1STLEVEL_SIZE (PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1)/ PTHREAD_KEY_2NDLEVEL_SIZE)其中在/usr/include/bits/local_lim.h中定義了PTHREAD_KEYS_MAX為1024,因此一維數(shù)組大小為32。而具體存放的位置由key值經(jīng)過以下計(jì)算得到:idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZEidx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE也就是說,數(shù)據(jù)存放與一個(gè)3232的稀疏矩陣中。同樣,訪問的時(shí)候也由key值經(jīng)過類似計(jì)算得到數(shù)據(jù)所在位置索引,再取出其中內(nèi)容返回。四 使用范例以下這個(gè)例子沒有什么實(shí)際意義,只是說明如何使用,以及能夠使用這一機(jī)制達(dá)到存儲(chǔ)線程私有數(shù)據(jù)的目的。#include #include pthread_key_t key;void echomsg(int t)printf(destructor excuted in thread %d,param=%dn,pthread_self(),t);void * child1(void *arg)int tid=pthread_self();printf(thread %d entern,tid);pthread_setspecific(key,(void *)tid);sleep(2);printf(thread %d returns %dn,tid,pthread_getspecific(key);sleep(5);void * child2(void *arg)int tid=pthread_self();printf(thread %d entern,tid);pthread_setspecific(key,(void *)tid);sleep(1);printf(thread %d returns %dn,tid,pthread_getspecific(key);sleep(5);int main(void)int tid1,tid2;printf(hellon);pthread_key_create(&key,echomsg);pthread_create(&tid1,NULL,child1,NULL);pthread_create(&tid2,NULL,child2,NULL);sleep(10);pthread_key_delete(key);printf(main thread exitn);return 0;給例程創(chuàng)建兩個(gè)線程分別設(shè)置同一個(gè)線程私有數(shù)據(jù)為自己的線程ID,為了檢驗(yàn)其私有性,程序錯(cuò)開了兩個(gè)線程私有數(shù)據(jù)的寫入和讀出的時(shí)間,從程序運(yùn)行結(jié)果可以看出,兩個(gè)線程對(duì)TSD的修改互不干擾。同時(shí),當(dāng)線程退出時(shí),清理函數(shù)會(huì)自動(dòng)執(zhí)行,參數(shù)為tid。Posix線程編程指南(3)內(nèi)容:一 互斥鎖二 條件變量三 信號(hào)燈四 異步信號(hào)五 其他同步方式關(guān)于作者相關(guān)內(nèi)容:(1) 線程創(chuàng)建與取消(2) 線程私有數(shù)據(jù)線程同步楊沙洲()2001 年 10 月這是一個(gè)關(guān)于Posix線程編程的專欄。作者在闡明概念的基礎(chǔ)上,將向您詳細(xì)講述Posix線程庫API。本文是第三篇將向您講述線程同步。一 互斥鎖盡管在Posix Thread中同樣可以使用IPC的信號(hào)量機(jī)制來實(shí)現(xiàn)互斥鎖mutex功能,但顯然semphore的功能過于強(qiáng)大了,在Posix Thread中定義了另外一套專門用于線程同步的mutex函數(shù)。1 創(chuàng)建和銷毀有兩種方法創(chuàng)建互斥鎖,靜態(tài)方式和動(dòng)態(tài)方式。POSIX定義了一個(gè)宏P(guān)THREAD_MUTEX_INITIALIZER來靜態(tài)初始化互斥鎖,方法如下:pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;在LinuxThreads實(shí)現(xiàn)中,pthread_mutex_t是一個(gè)結(jié)構(gòu),而PTHREAD_MUTEX_INITIALIZER則是一個(gè)結(jié)構(gòu)常量。動(dòng)態(tài)方式是采用pthread_mutex_init()函數(shù)來初始化互斥鎖,API定義如下:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)其中mutexattr用于指定互斥鎖屬性(見下),如果為NULL則使用缺省屬性。pthread_mutex_destroy()用于注銷一個(gè)互斥鎖,API定義如下:int pthread_mutex_destroy(pthread_mutex_t *mutex)銷毀一個(gè)互斥鎖即意味著釋放它所占用的資源,且要求鎖當(dāng)前處于開放狀態(tài)。由于在Linux中,互斥鎖并不占用任何資源,因此LinuxThreads中的pthread_mutex_destroy()除了檢查鎖狀態(tài)以外(鎖定狀態(tài)則返回EBUSY)沒有其他動(dòng)作。2 互斥鎖屬性互斥鎖的屬性在創(chuàng)建鎖的時(shí)候指定,在LinuxThreads實(shí)現(xiàn)中僅有一個(gè)鎖類型屬性,不同的鎖類型在試圖對(duì)一個(gè)已經(jīng)被鎖定的互斥鎖加鎖時(shí)表現(xiàn)不同。當(dāng)前(glibc2.2.3,linuxthreads0.9)有四個(gè)值可供選擇:PTHREAD_MUTEX_TIMED_NP,這是缺省值,也就是普通鎖。當(dāng)一個(gè)線程加鎖以后,其余請(qǐng)求鎖的線程將形成一個(gè)等待隊(duì)列,并在解鎖后按優(yōu)先級(jí)獲得鎖。這種鎖策略保證了資源分配的公平性。PTHREAD_MUTEX_RECURSIVE_NP,嵌套鎖,允許同一個(gè)線程對(duì)同一個(gè)鎖成功獲得多次,并通過多次unlock解鎖。如果是不同線程請(qǐng)求,則在加鎖線程解鎖時(shí)重新競(jìng)爭(zhēng)。PTHREAD_MUTEX_ERRORCHECK_NP,檢錯(cuò)鎖,如果同一個(gè)線程請(qǐng)求同一個(gè)鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動(dòng)作相同。這樣就保證當(dāng)不允許多次加鎖時(shí)不會(huì)出現(xiàn)最簡(jiǎn)單情況下的死鎖。PTHREAD_MUTEX_ADAPTIVE_NP,適應(yīng)鎖,動(dòng)作最簡(jiǎn)單的鎖類型,僅等待解鎖后重新競(jìng)爭(zhēng)。3 鎖操作鎖操作主要包括加鎖pthread_mutex_lock()、解鎖pthread_mutex_unlock()和測(cè)試加鎖pthread_mutex_trylock()三個(gè),不論哪種類型的鎖,都不可能被兩個(gè)不同的線程同時(shí)得到,而必須等待解鎖。對(duì)于普通鎖和適應(yīng)鎖類型,解鎖者可以是同進(jìn)程內(nèi)任何線程;而檢錯(cuò)鎖則必須由加鎖者解鎖才有效,否則返回EPERM;對(duì)于嵌套鎖,文檔和實(shí)現(xiàn)要求必須由加鎖者解鎖,但實(shí)驗(yàn)結(jié)果表明并沒有這種限制,這個(gè)不同目前還沒有得到解釋。在同一進(jìn)程中的線程,如果加鎖后沒有解鎖,則任何其他線程都無法再獲得鎖。int pthread_mutex_lock(pthread_mutex_t *mutex)int pthread_mutex_unlock(pthread_mutex_t *mutex)int pthread_mutex_trylock(pthread_mutex_t *mutex)pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經(jīng)被占據(jù)時(shí)返回EBUSY而不是掛起等待。4 其他POSIX線程鎖機(jī)制的Linux實(shí)現(xiàn)都不是取消點(diǎn),因此,延遲取消類型的線程不會(huì)因收到取消信號(hào)而離開加鎖等待。值得注意的是,如果線程在加鎖后解鎖前被取消,鎖將永遠(yuǎn)保持鎖定狀態(tài),因此如果在關(guān)鍵區(qū)段內(nèi)有取消點(diǎn)存在,或者設(shè)置了異步取消類型,則必須在退出回調(diào)函數(shù)中解鎖。這個(gè)鎖機(jī)制同時(shí)也不是異步信號(hào)安全的,也就是說,不應(yīng)該在信號(hào)處理過程中使用互斥鎖,否則容易造成死鎖。二 條件變量條件變量是利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制,主要包括兩個(gè)動(dòng)作:一個(gè)線程等待條件變量的條件成立而掛起;另一個(gè)線程使條件成立(給出條件成立信號(hào))。為了防止競(jìng)爭(zhēng),條件變量的使用總是和一個(gè)互斥鎖結(jié)合在一起。1 創(chuàng)建和注銷條件變量和互斥鎖一樣,都有靜態(tài)動(dòng)態(tài)兩種創(chuàng)建方式,靜態(tài)方式使用PTHREAD_COND_INITIALIZER常量,如下:pthread_cond_t cond=PTHREAD_COND_INITIALIZER動(dòng)態(tài)方式調(diào)用pthread_cond_init()函數(shù),API定義如下:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)盡管POSIX標(biāo)準(zhǔn)中為條件變量定義了屬性,但在LinuxThreads中沒有實(shí)現(xiàn),因此cond_attr值通常為NULL,且被忽略。注銷一個(gè)條件變量需要調(diào)用pthread_cond_destroy(),只有在沒有線程在該條件變量上等待的時(shí)候才能注銷這個(gè)條件變量,否則返回EBUSY。因?yàn)長(zhǎng)inux實(shí)現(xiàn)的條件變量沒有分配什么資源,所以注銷動(dòng)作只包括檢查是否有等待線程。API定義如下:int pthread_cond_destroy(pthread_cond_t *cond)2 等待和激發(fā)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)等待條件有兩種方式:無條件等待pthread_cond_wait()和計(jì)時(shí)等待pthread_cond_timedwait(),其中計(jì)時(shí)等待方式如果在給定時(shí)刻前條件沒有滿足,則返回ETIMEOUT,結(jié)束等待,其中abstime以與time()系統(tǒng)調(diào)用相同意義的絕對(duì)時(shí)間形式出現(xiàn),0表示格林尼治時(shí)間1970年1月1日0時(shí)0分0秒。無論哪種等待方式,都必須和一個(gè)互斥鎖配合,以防止多個(gè)線程同時(shí)請(qǐng)求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的競(jìng)爭(zhēng)條件(Race Condition)。mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應(yīng)鎖(PTHREAD_MUTEX_ADAPTIVE_NP),且在調(diào)用pthread_cond_wait()前必須由本線程加鎖(pthread_mutex_lock()),而在更新條件等待隊(duì)列以前,mutex保持鎖定狀態(tài),并在線程掛起進(jìn)入等待前解鎖。在條件滿足從而離開pthread_cond_wait()之前,mutex將被重新加鎖,以與進(jìn)入pthread_cond_wait()前的加鎖動(dòng)作對(duì)應(yīng)。激發(fā)條件有兩種形式,pthread_cond_signal()激活一個(gè)等待該條件的線程,存在多個(gè)等待線程時(shí)按入隊(duì)順序激活其中一個(gè);而pthread_cond_broadcast()則激活所有等待線程。3 其他pthread_cond_wait()和pthread_cond_timedwait()都被實(shí)現(xiàn)為取消點(diǎn),因此,在該處等待的線程將立即重新運(yùn)行,在重新鎖定mutex后離開pthread_cond_wait(),然后執(zhí)行取消動(dòng)作。也就是說如果pthread_cond_wait()被取消,mutex是保持鎖定狀態(tài)的,因而需要定義退出回調(diào)函數(shù)來為其解鎖。以下示例集中演示了互斥鎖和條件變量的結(jié)合使用,以及取消對(duì)于條件等待動(dòng)作的影響。在例子中,有兩個(gè)線程被啟動(dòng),并等待同一個(gè)條件變量,如果不使用退出回調(diào)函數(shù)(見范例中的注釋部分),則tid2將在pthread_mutex_lock()處永久等待。如果使用回調(diào)函數(shù),則tid2的條件等待及主線程的條件激發(fā)都能正常工作。#include #include #include pthread_mutex_t mutex;pthread_cond_t cond;void * child1(void *arg)pthread_cleanup_push(pthread_mutex_unlock,&mutex); /* comment 1 */while(1)printf(thread 1 get running n);printf(thread 1 pthread_mutex_lock returns %dn,pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);printf(thread 1 condition appliedn);pthread_mutex_unlock(&mutex);sleep(5);pthread_cleanup_pop(0); /* comment 2 */void *child2(void *arg)while(1)sleep(3); /* comment 3 */printf(thread 2 get running.n);printf(thread 2 pthread_mutex_lock returns %dn,pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);printf(thread 2 condition appliedn);pthread_mutex_unlock(&mutex);sleep(1);int main(void)int tid1,tid2;printf(hello, condition variable testn);pthread_mutex_init(&mutex,NULL);pthread_cond_init(&cond,NULL);pthread_create(&tid1,NULL,child1,NULL);pthread_create(&tid2,NULL,child2,NULL);dosleep(2); /* comment 4 */pthread_cancel(tid1); /* comment 5 */sleep(2); /* comment 6 */pthread_cond_signal(&cond);while(1);sleep(100);pthread_exit(0);如果不做注釋5的pthread_cancel()動(dòng)作,即使沒有那些sleep()延時(shí)操作,child1和child2都能正常工作。注釋3和注釋4的延遲使得child1有時(shí)間完成取消動(dòng)作,從而使child2能在child1退出之后進(jìn)入請(qǐng)求鎖操作。如果沒有注釋1和注釋2的回調(diào)函數(shù)定義,系統(tǒng)將掛起在child2請(qǐng)求鎖的地方;而如果同時(shí)也不做注釋3和注釋4的延時(shí),child2能在child1完成取消動(dòng)作以前得到控制,從而順利執(zhí)行申請(qǐng)鎖的操作,但卻可能掛起在pthread_cond_wait()中,因?yàn)槠渲幸灿猩暾?qǐng)mutex的操作。child1函數(shù)給出的是標(biāo)準(zhǔn)的條件變量的使用方式:回調(diào)函數(shù)保護(hù),等待條件前鎖定,pthread_cond_wait()返回后解鎖。條件變量機(jī)制不是異步信號(hào)安全的,也就是說,在信號(hào)處理函數(shù)中調(diào)用pthread_cond_signal()或者pthread_cond_broadcast()很可能引起死鎖。三 信號(hào)燈信號(hào)燈與互斥鎖和條件變量的主要不同在于燈的概念,燈亮則意味著資源可用,燈滅則意味著不可用。如果說后兩中同步方式側(cè)重于等待操作,即資源不可用的話,信號(hào)燈機(jī)制則側(cè)重于點(diǎn)燈,即告知資源可用;沒有等待線程的解鎖或激發(fā)條件都是沒有意義的,而沒有等待燈亮的線程的點(diǎn)燈操作則有效,且能保持燈亮狀態(tài)。當(dāng)然,這樣的操作原語也意味著更多的開銷。信號(hào)燈的應(yīng)用除了燈亮/燈滅這種二元燈以外,也可以采用大于1的燈數(shù),以表示資源數(shù)大于1,這時(shí)可以稱之為多元燈。1 創(chuàng)建和注銷POSIX信號(hào)燈標(biāo)準(zhǔn)定義了有名信號(hào)燈和無名信號(hào)燈兩種,但LinuxThreads的實(shí)現(xiàn)僅有無名燈,同時(shí)有名燈除了總是可用于多進(jìn)程之間以外,在使用上與無名燈并沒有很大的區(qū)別,因此下面僅就無名燈進(jìn)行討論。int sem_init(sem_t *sem, int pshared, unsigned int value)這是創(chuàng)建信號(hào)燈的API,其中value為信號(hào)燈的初值,pshared表示是否為多進(jìn)程共享而不僅僅是用于一個(gè)進(jìn)程。LinuxThreads沒有實(shí)現(xiàn)多進(jìn)程共享信號(hào)燈,因此所有非0值的pshared輸入都將使sem_init()返回-1,且置errno為ENOSYS。初始化好的信號(hào)燈由sem變量表征,用于以下點(diǎn)燈、滅燈操作。int sem_destroy(sem_t * sem)被注銷的信號(hào)燈sem要求已沒有線程在等待該信號(hào)燈,否則返回-1,且置errno為EBUSY。除此之外,LinuxThreads的信號(hào)燈注銷函數(shù)不做其他動(dòng)作。2 點(diǎn)燈和滅燈int sem_post(sem_t * sem)點(diǎn)燈操作將信號(hào)燈值原子地加1,表示增加一個(gè)可訪問的資源。int sem_wait(sem_t * sem)int sem_trywait(sem_t * sem)sem_wait()為等待燈亮操作,等待燈亮(信號(hào)燈值大于0),然后將信號(hào)燈原子地減1,并返回。sem_trywait()為sem_wait()的非阻塞版,如果信號(hào)燈計(jì)數(shù)大于0,則原子地減1并返回0,否則立即返回-1,errno置為EAGAIN。3 獲取燈值int sem_getvalue(sem_t * sem, int * sval)讀取sem中的燈計(jì)數(shù),存于*sval中,并返回0。4 其他sem_wait()被實(shí)現(xiàn)為取消點(diǎn),而且在支持原子比較且交換指令的體系結(jié)構(gòu)上,sem_post()是唯一能用于異步信號(hào)處理函數(shù)的POSIX異步信號(hào)安全的API。四 異步信號(hào)由于LinuxThreads是在核外使用核內(nèi)輕量級(jí)進(jìn)程實(shí)現(xiàn)的線程,所以基于內(nèi)核的異步信號(hào)操作對(duì)于線程也是有效的。但同時(shí),由于異步信號(hào)總是實(shí)際發(fā)往某個(gè)進(jìn)程,所以無法實(shí)現(xiàn)POSIX標(biāo)準(zhǔn)所要求的信號(hào)到達(dá)某個(gè)進(jìn)程,然后再由該進(jìn)程將信號(hào)分發(fā)到所有沒有阻塞該信號(hào)的線程中原語,而是只能影響到其中一個(gè)線程。POSIX異步信號(hào)同時(shí)也是一個(gè)標(biāo)準(zhǔn)C庫提供的功能,主要包括信號(hào)集管理(sigemptyset()、sigfillset()、sigaddset()、sigdelset()、sigismember()等)、信號(hào)處理函數(shù)安裝(sigaction())、信號(hào)阻塞控制(sigprocmask())、被阻塞信號(hào)查詢(sigpending())、信號(hào)等待(sigsuspend()等,它們與發(fā)送信號(hào)的kill()等函數(shù)配合就能實(shí)現(xiàn)進(jìn)程間異步信號(hào)功能。LinuxThreads圍繞線程封裝了sigaction()何raise(),本節(jié)集中討論LinuxThreads中擴(kuò)展的異步信號(hào)函數(shù),包括pthread_sigmask()、pthread_kill()和sigwait()三個(gè)函數(shù)。毫無疑問,所有POSIX異步信號(hào)函數(shù)對(duì)于線程都是可用的。int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask)設(shè)置線程的信號(hào)屏蔽碼,語義與sigprocmask()相同,但對(duì)不允許屏蔽的Cancel信號(hào)和不允許響應(yīng)的Restart信號(hào)進(jìn)行了保護(hù)。被屏蔽的信號(hào)保存在信號(hào)隊(duì)列中,可由sigpending()函數(shù)取出。int pthread_kill(pthread_t thread, int signo)向thread號(hào)線程發(fā)送signo信號(hào)。實(shí)現(xiàn)中在通過thread線程號(hào)定位到對(duì)應(yīng)進(jìn)程號(hào)以后使用kill()系統(tǒng)調(diào)用完成發(fā)送。int sigwait(const sigset_t *set, int *sig)掛起線程,等待set中指定的信號(hào)之一到達(dá),并將到達(dá)的信號(hào)存入*sig中。POSIX標(biāo)準(zhǔn)建議在調(diào)用sigwait()等待信號(hào)以前,進(jìn)程中所有線程都應(yīng)屏蔽該信號(hào),以保證僅有sigwait()的調(diào)用者獲得該信號(hào),因此,對(duì)于需要等待同步的異步信號(hào),總是應(yīng)該在創(chuàng)建任何線程以前調(diào)用pthread_sigmask()屏蔽該信號(hào)的處理。而且,調(diào)用sigwait()期間,原來附接在該信號(hào)上的信號(hào)處理函數(shù)不會(huì)被調(diào)用。如果在等待期間接收到Cancel信號(hào),則立即退出等待,也就是說sigwait()被實(shí)現(xiàn)為取消點(diǎn)。五 其他同步方式除了上述討論的同步方式以外,其他很多進(jìn)程間通信手段對(duì)于LinuxThreads也是可用的,比如基于文件系統(tǒng)的IPC(管道、Unix域Socket等)、消息隊(duì)列(Sys.V或者Posix的)、System V的信號(hào)燈等。只有一點(diǎn)需要注意,LinuxThreads在核內(nèi)是作為共享存儲(chǔ)區(qū)、共享文件系統(tǒng)屬性、共享信號(hào)處理、共享文件描述符的獨(dú)立進(jìn)程看待的。Posix線程編程指南(4)內(nèi)容:1 線程終止方式2 線程終止時(shí)的清理3 線程終止的同步及其返回值4 關(guān)于pthread_exit()和return參考資料關(guān)于作者相關(guān)內(nèi)容:(1) 線程創(chuàng)建與取消(2) 線程私有數(shù)據(jù)(3) 線程同步線程終止楊沙洲()2001 年 11 月這是一個(gè)關(guān)于Posix線程編程的專欄。作者在闡明概念的基礎(chǔ)上,將向您詳細(xì)講述Posix線程庫API。本文是第四篇將向您講述線程中止。1 線程終止方式一般來說,Posix的線程終止有兩種情況:正常終止和非正常終止。線程主動(dòng)調(diào)用pthread_exit()或者從線程函數(shù)中return都將使線程正常退出,這是可預(yù)見的退出方式;非正常終止是線程在其他線程的干預(yù)下,或者由于自身運(yùn)行出錯(cuò)(比如訪問非法地址)而退出,這種退出方式是不可預(yù)見的。2 線程終止時(shí)的清理不論是可預(yù)見的線程終止還是異常終止,都會(huì)存在資源釋放的問題,在不考慮因運(yùn)行出錯(cuò)而退出的前提下,如何保證線程終止時(shí)能順利的釋放掉自己所占用的資源,特別是鎖資源,就是一個(gè)必須考慮解決的問題。最經(jīng)常出現(xiàn)的情形是資源獨(dú)占鎖的使用:線程為了訪問臨界資源而為其加上鎖,但在訪問過程中被外界取消,如果線程處于響應(yīng)取消狀態(tài),且采用異步方式響應(yīng),或者在打開獨(dú)占鎖以前的運(yùn)行路徑上存在取消點(diǎn),則該臨界資源將永遠(yuǎn)處于鎖定狀態(tài)得不到釋放。外界取消操作是不可預(yù)見的,因此的確需要一個(gè)機(jī)制來簡(jiǎn)化用于資源釋放的編程。在POSIX線程API中提供了一個(gè)pthread_cleanup_push()/pthread_cleanup_pop()函數(shù)對(duì)用于自動(dòng)釋放資源-從pthread_cleanup_push()的調(diào)用點(diǎn)到pthread_cleanup_pop()之間的程序段中的終止動(dòng)作(包括調(diào)用pthread_exit()和取消點(diǎn)終止)都將執(zhí)行pthread_cleanup_push()所指定的清理函數(shù)。API定義如下:void pthread_cleanup_push(void (*routine) (void *), void *arg)void pthread_cleanup_pop(int execute)pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的棧結(jié)構(gòu)管理,void routine(void *arg)函數(shù)在調(diào)用pthread_cleanup_push()時(shí)壓入清理函數(shù)棧,多次對(duì)pthread_cleanup_push()的調(diào)用將在清理函數(shù)棧中形成一個(gè)函數(shù)鏈,在執(zhí)行該函數(shù)鏈時(shí)按照壓棧的相反順序彈出。execute參數(shù)表示執(zhí)行到pthread_cleanup_pop()時(shí)是否在彈出清理函數(shù)的同時(shí)執(zhí)行該函數(shù),為0表示不執(zhí)行,非0為執(zhí)行;這個(gè)參數(shù)并不影響異常終止時(shí)清理函數(shù)的執(zhí)行。pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式實(shí)現(xiàn)的,這是pthread.h中的宏定義:#define pthread_cleanup_push(routine,arg) struct _pthread_cleanup_buffer _buffer; _pthread_cleanup_push (&_buffer, (routine), (arg);#define pthread_cleanup_pop(execute) _pthread_cleanup_pop (&_buffer, (execute); 可見,pthread_cleanup_push()帶有一個(gè),而pthread_cleanup_pop()帶有一個(gè),因此這兩個(gè)函數(shù)必須成對(duì)出現(xiàn),且必須位于程序的同一級(jí)別的代碼段中才能通過編譯。在下面的例子里,當(dāng)線程在do some work中終止時(shí),將主動(dòng)調(diào)用pthread_mutex_unlock(mut),以完成解鎖動(dòng)作。pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);pthread_mutex_lock(&mut);/* do some work */pthread_mutex_unlock(&mut);pthread_cleanup_pop(0);必須要注意的是,如果線程處于PTHREAD_CANCEL_ASYNCHRONOUS狀態(tài),上述代碼段就有可能出錯(cuò),因?yàn)镃ANCEL事件有可能在pthread_cleanup_push()和pthread_mutex_lock()之間發(fā)生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之間發(fā)生,從而導(dǎo)致清理函數(shù)unlock一個(gè)并沒有加鎖的mutex變量,造成錯(cuò)誤。因此,在使用清理函數(shù)的時(shí)候,都應(yīng)該暫時(shí)設(shè)置成PTHREAD_CANCEL_DEFERRED模式。為此,POSIX的Linux實(shí)現(xiàn)中還提供了一對(duì)不保證可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()擴(kuò)展函數(shù),功能與以下代碼段相當(dāng): int oldtype;pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);pthread_cleanup_push(routine, arg);.pthread_cleanup_pop(execute);pthread_setcancel

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論