版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第10章線程控制
計(jì)算機(jī)在運(yùn)行時(shí)其執(zhí)行過程從大到小可以分為作業(yè)、進(jìn)程和線程3個(gè)級(jí)別。線程是系統(tǒng)分配處理器時(shí)間資源的基本單元,或者說是進(jìn)程之內(nèi)獨(dú)立執(zhí)行的一個(gè)單元。對(duì)于操作系統(tǒng)而言,其調(diào)度單元是線程。Linux中的線程是輕量級(jí)線程(lightweightthread),Linux中的線程調(diào)度是由內(nèi)核調(diào)度程序完成的,每個(gè)線程都有自己的ID號(hào)。與進(jìn)程相比,線程所消耗的系統(tǒng)資源較少、創(chuàng)建較快、相互間的通信也比較容易。
Linux線程10.1創(chuàng)建線程
10.2線程屬性
10.3線程等待終止10.4私有數(shù)據(jù)10.5線程同步10.6出錯(cuò)處理10.7思考與練習(xí)
10.810.1Linux線程線程是在共享內(nèi)存空間中并發(fā)的多道執(zhí)行路徑,它們共享一個(gè)進(jìn)程的資源。在兩個(gè)普通進(jìn)程(非線程)間進(jìn)行切換時(shí),內(nèi)核準(zhǔn)備從一個(gè)進(jìn)程的上下文切換到另一個(gè)進(jìn)程的上下文要有很大的花費(fèi),包括保存老進(jìn)程CPU狀態(tài),并加載新進(jìn)程的保存狀態(tài),用新進(jìn)程的內(nèi)存映像替換老進(jìn)程的內(nèi)存映像。線程也有其私有數(shù)據(jù)信息,包括:線程號(hào)(threadID):每個(gè)線程都有一個(gè)唯一的線程號(hào)與之對(duì)應(yīng)。寄存器(包括程序計(jì)數(shù)器和堆棧指針)。堆棧信號(hào)掩碼優(yōu)先級(jí)線程私有的存儲(chǔ)空間。多線程具有以下的優(yōu)點(diǎn):(1)提高應(yīng)用程序的響應(yīng)速度。將耗時(shí)長的操作置于一個(gè)新的線程,可以避免系統(tǒng)的等待。(2)使多CPU系統(tǒng)更有效。操作系統(tǒng)會(huì)保證當(dāng)線程數(shù)目不大于CPU數(shù)目時(shí)不同的線程運(yùn)行于不同的CPU上。(3)改善程序結(jié)構(gòu)。一個(gè)長而復(fù)雜的進(jìn)程可以分為多個(gè)線程,成為獨(dú)立運(yùn)行的部分。10.1.1線程和進(jìn)程的關(guān)系Linux是支持多線程的,在一個(gè)進(jìn)程內(nèi)生成多個(gè)線程。一個(gè)進(jìn)程可以擁有一個(gè)或多個(gè)線程。線程和進(jìn)程二者之間的關(guān)系有以下幾點(diǎn)。(1)首先,線程采用了多個(gè)線程可共享資源的設(shè)計(jì)思想。在多進(jìn)程情況下,每個(gè)進(jìn)程都有自己獨(dú)立的地址空間,在多線程情況下,同一進(jìn)程內(nèi)的線程共享進(jìn)程的地址空間。線程和進(jìn)程的最大區(qū)別在于線程完全共享相同的地址空間,運(yùn)行在同一地址上。(2)其次,由于進(jìn)程地址空間獨(dú)立而線程共享地址空間,所以從一個(gè)線程切換到另一線程所花費(fèi)的代價(jià)比進(jìn)程低。(3)再次,進(jìn)程本身的信息在內(nèi)存中占用的空間比線程大。因此,線程更能充分地利用內(nèi)存。線程可以看作是在進(jìn)程內(nèi)部執(zhí)行的指定序列。(4)最后,線程間的通信比進(jìn)程間的通信更加方便和省時(shí)。進(jìn)程間的數(shù)據(jù)空間相互獨(dú)立,彼此通信要以專門的通信方式進(jìn)行,通信時(shí)必須經(jīng)過操作系統(tǒng),而同一進(jìn)程的多個(gè)線程共享數(shù)據(jù)空間,一個(gè)線程的數(shù)據(jù)可以直接提供給其他線程使用,不必進(jìn)過操作系統(tǒng)。10.1.2線程分類內(nèi)核線程Linux內(nèi)核可以看作一個(gè)服務(wù)進(jìn)程(管理軟硬件資源,響應(yīng)用戶進(jìn)程的種種合理以及不合理的請(qǐng)求)。內(nèi)核需要多個(gè)執(zhí)行流并行,為了防止可能的阻塞,多線程化是必要的。內(nèi)核線程就是內(nèi)核的分身,一個(gè)分身可以處理一件特定事情。Linux內(nèi)核使用內(nèi)核線程來將內(nèi)核分成幾個(gè)功能模塊,像kswapd、kflushd等,這在處理異步事件如異步IO時(shí)特別有用。內(nèi)核線程的使用是廉價(jià)的,唯一使用的資源就是內(nèi)核棧和上下文切換時(shí)保存寄存器的空間。支持多線程的內(nèi)核叫做多線程內(nèi)核(Multi-Threadskernel)。內(nèi)核線程的調(diào)度由內(nèi)核負(fù)責(zé),一個(gè)內(nèi)核線程處于阻塞狀態(tài)時(shí)不影響其他的內(nèi)核線程,因?yàn)槠涫钦{(diào)度的基本單位。這與用戶線程是不一樣的。用戶線程用戶線程在用戶空間中實(shí)現(xiàn),允許多線程的程序運(yùn)行時(shí)不需要特定的內(nèi)核支持,內(nèi)核不需要直接對(duì)用戶線程進(jìn)程調(diào)度,內(nèi)核的調(diào)度對(duì)象和傳統(tǒng)進(jìn)程一樣,還是進(jìn)程本身,內(nèi)核并不知道用戶線程的存在。用戶線程的優(yōu)點(diǎn)如下:(1)某些線程操作的系統(tǒng)消耗大大減少。比如,對(duì)屬于同一個(gè)進(jìn)程的線程之間進(jìn)行調(diào)度切換時(shí),不需要調(diào)用系統(tǒng)調(diào)用,因此將減少額外的消耗,一個(gè)進(jìn)程往往可以啟動(dòng)上千個(gè)線程。(2)用戶線程的實(shí)現(xiàn)方式可以被定制或修改,以適應(yīng)特殊應(yīng)用的要求。它對(duì)于多媒體實(shí)時(shí)過程等尤其有用。另外,用戶線程可以比內(nèi)核線程實(shí)現(xiàn)方法默認(rèn)情況支持更多的線程。
系統(tǒng)創(chuàng)建線程的順序如下:當(dāng)一個(gè)進(jìn)程啟動(dòng)后,它會(huì)自動(dòng)創(chuàng)建一個(gè)線程即主線程(mainthread)或者初始化線程(initialthread),然后利用pthread_initialize()初始化系統(tǒng)管理線程并且啟動(dòng)線程機(jī)制。線程機(jī)制啟動(dòng)以后需要?jiǎng)?chuàng)建線程,需要phread_create()向管理線程發(fā)送REQ_CREATE請(qǐng)求,調(diào)用pthread_handle_create()創(chuàng)建新線程。10.2創(chuàng)建線程
線程的創(chuàng)建通過函數(shù)pthread_create()來完成,該函數(shù)的原型如下:#include<pthread.h>int
pthread_create(pthread_t*thread,pthread_attr_t
*attr,void*(*start_routine)(void*),void*arg);該函數(shù)創(chuàng)建線程,并為其分配一個(gè)唯一的標(biāo)識(shí)符pthread_t。調(diào)用者提供一個(gè)將由該線程執(zhí)行的函數(shù),該調(diào)用還可以為線程顯式指定一些屬性。表10-1創(chuàng)建線程其他系統(tǒng)函數(shù)函數(shù)說明pthread_t
pthread_self(void)獲取本線程的線程IDint
pthread_equal(pthread_tthread1,pthread_tthread2)判斷兩個(gè)線程ID是否指向同一線程int
pthread_once(pthread_once_t*once_control,void(*init_routine)(void))用來保證init_routine線程函數(shù)在進(jìn)程中僅執(zhí)行一次?!纠?0-1】創(chuàng)建線程。設(shè)計(jì)步驟[1]在Vim中創(chuàng)建一個(gè)新工程文件,命名為“example10_1.c”。[2]在“example10_1.c”中創(chuàng)建代碼如下所示。#include<stdlib.h>#include<stdio.h>#include<pthread.h>voidthread(){inti;for(i=0;i<3;i++)printf("Thisisapthread.\n");}intmain(){pthread_tid;int
i,ret;ret=pthread_create(&id,NULL,(void*)thread,NULL);if(ret!=0){printf("Createpthreaderror!\n");exit(1);}for(i=0;i<3;i++)printf("Thisisthemainprocess.\n");pthread_join(id,NULL);return(0);}用GCC編譯運(yùn)行程序結(jié)果如圖10-1所示。圖10-1創(chuàng)建線程10.3線程屬性
創(chuàng)建線程函數(shù)pthread_create()的第二個(gè)參數(shù)用來指定線程的屬性。線程屬性主要有綁定屬性、分離屬性、堆棧地址、堆棧大小、優(yōu)先級(jí)等。其中系統(tǒng)默認(rèn)的是非邦定、非分離、缺省1M的堆棧、與父進(jìn)程同樣級(jí)別的優(yōu)先級(jí)。(1)綁定屬性。在Linux中,采用的是“一對(duì)一”的線程機(jī)制。也就是一個(gè)用戶線程對(duì)應(yīng)一個(gè)內(nèi)核線程。綁定屬性就是指一個(gè)用戶線程固定地分配給一個(gè)內(nèi)核線程,因?yàn)镃PU時(shí)間片的調(diào)度是面向內(nèi)核線程(輕量級(jí)進(jìn)程)的,因此具有綁定屬性的線程可以保證在需要的時(shí)候總有一個(gè)內(nèi)核線程與之對(duì)應(yīng),而與之對(duì)應(yīng)的非綁定屬性就是指用戶線程和內(nèi)核線程的關(guān)系不是始終固定的,而是由系統(tǒng)來分配。(2)分離屬性。分離屬性是決定線程以一個(gè)什么樣的方式來終止自己。在非分離情況下,當(dāng)一個(gè)線程結(jié)束時(shí),它多占用的線程沒有得到釋放,也就是沒有真正的終止,需要通過pthread_join來釋放資源。而在分離屬性情況下,一個(gè)線程結(jié)束時(shí)會(huì)立即釋放它所占有的系統(tǒng)資源。但這里有一點(diǎn)要注意的是,如果設(shè)置一個(gè)線程分離屬性,而這個(gè)線程又運(yùn)行得非??斓脑?,那么它很可能在pthread_create函數(shù)返回之前就終止了線程函數(shù)的運(yùn)行,它終止以后就很有可能將線程號(hào)和系統(tǒng)資源移交給其他的線程使用,這時(shí)調(diào)用pthread_create()的線程就得到錯(cuò)誤的線程號(hào)。1.設(shè)置/獲取detachstate
屬性detachstate
屬性表示新創(chuàng)建的線程與進(jìn)程中其它線程是處于分離狀態(tài)還是可連接狀態(tài)。其合法值包括:PTHREAD_CREATE_DETACHED:此選項(xiàng)使得使用attr
創(chuàng)建的所有線程處于分離狀態(tài)。線程終止時(shí),系統(tǒng)將自動(dòng)回收與帶有此狀態(tài)的線程相關(guān)聯(lián)的資源。調(diào)用使用此屬性創(chuàng)建的pthread_detach()或pthread_join()函數(shù)將導(dǎo)致錯(cuò)誤。PTHREAD_CREATE_JOINABLE:此選項(xiàng)使得使用attr
創(chuàng)建的所有線程處于可連接狀態(tài)。線程終止時(shí),不會(huì)回收與帶有此狀態(tài)的線程相關(guān)聯(lián)的資源。要回收系統(tǒng)資源應(yīng)用程序必須調(diào)用使用此屬性創(chuàng)建的線程的pthread_detach()或pthread_join()函數(shù)。detachstate
的缺省值是PTHREAD_CREATE_JOINABLE。設(shè)置線程的detachstate
屬性的函數(shù)聲明如下:int
pthread_attr_setdetachstate(pthread_attr_t*attr,int
detachstate);/*用于設(shè)置已初始化屬性對(duì)象attr
中的detachstate
屬性*/獲取線程detachstate屬性的函數(shù)聲明如下:int
pthread_attr_getdetachstate(pthread_attr_t*attr,int*detachstate);/*獲取detachstate
屬性*/2.設(shè)置/獲取guardsize
屬性guardsize
屬性允許應(yīng)用程序指定使用此屬性對(duì)象創(chuàng)建的線程的守護(hù)區(qū)大小。所指定的守護(hù)區(qū)大小的單位為字節(jié)。大多數(shù)系統(tǒng)將守護(hù)區(qū)大小向上舍入為系統(tǒng)可配置變量PAGESIZE的倍數(shù)。如果指定了零值,則不會(huì)創(chuàng)建守護(hù)區(qū)。為應(yīng)用程序提供了guardsize
屬性的作用為:溢出保護(hù)可能會(huì)導(dǎo)致系統(tǒng)資源浪費(fèi)。如果應(yīng)用程序創(chuàng)建大量線程,并且已知這些線程永遠(yuǎn)不會(huì)溢出其棧,則可以關(guān)閉溢出保護(hù)區(qū)。通過關(guān)閉溢出保護(hù)區(qū),可以節(jié)省系統(tǒng)資源。l線程在棧上分配大型數(shù)據(jù)結(jié)構(gòu)時(shí),可能需要較大的溢出保護(hù)區(qū)來檢測棧溢出。設(shè)置線程的guardsize屬性的函數(shù)聲明如下:int
pthread_attr_setguardsize(pthread_attr_t*attr,size_t
guardsize);/*用于設(shè)置已初始化屬性對(duì)象attr中的guardsize屬性值*/獲取線程guardsize屬性的函數(shù)聲明如下:int
pthread_attr_getguardsize(pthread_attr_t*attr,size_t*guardsize);/*獲取guardsize屬性*/3.設(shè)置/獲取schedparam
屬性schedparam
屬性的合法值因調(diào)度策略的不同而異。對(duì)于SCHED_FIFO和SCHED_RR調(diào)度策略,只需要schedparam屬性的sched_priority
成員。可以通過sched_get_priority_max()和sched_get_priority_min()獲取sched_priority
的合法值范圍。其他調(diào)度策略的schedparam的必要內(nèi)容是不確定的。設(shè)置線程的schedparam屬性的函數(shù)聲明如下:int
pthread_attr_setschedparam(pthread_attr_t*attr,struct
sched_param
schedparam);/*用于設(shè)置已初始化屬性對(duì)象attr
中的schedparam
屬性*/獲取線程schedparam屬性的函數(shù)聲明如下:int
pthread_attr_getschedparam(pthread_attr_t*attr,struct
sched_param*schedparam);/*獲取schedparam屬性*/4.設(shè)置/獲取schedpolicy
屬性schedpolicy
屬性允許通過此屬性對(duì)象創(chuàng)建的線程使用特定的調(diào)度策略,包括SCHED_OTHER(正常、非實(shí)時(shí))、SCHED_RR(實(shí)時(shí)、輪轉(zhuǎn)法)和SCHED_FIFO(實(shí)時(shí)、先入先出)等3種,缺省為SCHED_OTHER。設(shè)置線程的schedpolicy屬性的函數(shù)聲明如下:int
pthread_attr_setschedpolicy(pthread_attr_t*attr,intpolicy);/*用于設(shè)置已初始化屬性對(duì)象attr
中的schedpolicy
屬性*/獲取線程schedpolicy屬性的函數(shù)聲明如下:int
pthread_attr_getschedpolicy(pthread_attr_t*attr,int*policy);/*獲取schedpolicy
屬性*/5.設(shè)置/獲取inheritsched
屬性inheritsched
屬性選用是從創(chuàng)建線程中繼承還是從此屬性對(duì)象中獲得調(diào)度策略及關(guān)聯(lián)屬性。inheritsched
有兩種值可供選擇:PTHREAD_INHERIT_SCHED:此選項(xiàng)指定從創(chuàng)建線程中繼承調(diào)度策略及關(guān)聯(lián)屬性。如果使用attr
創(chuàng)建了線程,將忽略attr
參數(shù)中的調(diào)度策略和關(guān)聯(lián)屬性。PTHREAD_EXPLICIT_SCHED:此選項(xiàng)指定,從此屬性對(duì)象中獲得已創(chuàng)建線程的調(diào)度策略及關(guān)聯(lián)屬性。設(shè)置線程的inheritsched屬性的函數(shù)聲明如下:int
pthread_attr_setinheritsched(pthread_attr_t*attr,intinherit);/*用于設(shè)置已初始化屬性對(duì)象attr
中的inheritsched
屬性*/獲取線程inheritsched屬性的函數(shù)聲明如下:int
pthread_attr_getinheritsched(pthread_attr_t*attr,int*inherit)/*獲取inheritsched屬性*/6.設(shè)置/獲取scope屬性scope屬性用來設(shè)置創(chuàng)建線程的競爭范圍,其合法值包括:PTHREAD_SCOPE_SYSTEM:使用此競爭范圍創(chuàng)建的線程,將與系統(tǒng)中(以及同一調(diào)度域中)的其他線程競爭資源。此屬性一般用于表示用戶線程應(yīng)該直接綁定到內(nèi)核調(diào)度實(shí)體。PTHREAD_SCOPE_PROCESS:使用此競爭范圍創(chuàng)建的線程,將直接與其進(jìn)程內(nèi)使用此調(diào)度競爭范圍創(chuàng)建的其他線程爭用資源。此屬性一般用于表示不應(yīng)綁定用戶線程(不綁定到任何特定的內(nèi)核調(diào)度的實(shí)體)。設(shè)置線程的scope屬性的函數(shù)聲明如下:int
pthread_attr_setscope(pthread_attr_t*attr,intscope);/*用于設(shè)置已初始化屬性對(duì)象attr
中的contentionscope
屬性*/獲取線程scope屬性的函數(shù)聲明如下:int
pthread_attr_getscope(pthread_attr_t*attr,int*scope); /*獲取scope屬性*/7.設(shè)置/獲取stackaddr屬性此屬性選項(xiàng)指定創(chuàng)建的線程將要使用的堆棧地址。應(yīng)用程序全面負(fù)責(zé)這些堆棧的分配、管理和取消分配。存儲(chǔ)分配的選項(xiàng)為malloc(3C)、brk(2)和mmap(2)函數(shù)。如果使用此選項(xiàng),則只能使用此屬性對(duì)象創(chuàng)建一個(gè)線程。如果創(chuàng)建了多個(gè)線程,它們將使用同一個(gè)堆棧。stackaddr
屬性的缺省值為NULL。如果線程用戶堆棧的存儲(chǔ)不是由庫分配的(也就是說,stackaddr
屬性不是NULL),將忽略guardsize
屬性。設(shè)置線程的stackaddr屬性的函數(shù)聲明如下:int
pthread_attr_setstackaddr(pthread_attr_t*attr,void*stackaddr);/*用于設(shè)置已初始化屬性對(duì)象attr
中的stackaddr
屬性*/獲取線程stackaddr屬性的函數(shù)聲明如下:int
pthread_attr_getstackaddr(pthread_attr_t*attr,void**stackaddr);/*獲取stackaddr屬性*/8.設(shè)置/獲取stacksize屬性stacksize
屬性用來設(shè)置創(chuàng)建線程的用戶堆棧大小。其合法值包括:PTHREAD_STACK_MIN:此選項(xiàng)指定,使用此屬性對(duì)象創(chuàng)建的線程的用戶堆棧大小將使用缺省堆棧大小。此值為某個(gè)線程所需的最小堆棧大?。ㄒ宰止?jié)為單位)。對(duì)于所有線程來說,這個(gè)最小值可能無法接受。
stacksize:具體的大小。定義使用此屬性對(duì)象創(chuàng)建的線程的用戶堆棧大?。ㄒ宰止?jié)為單位)。此值必須大于或等于最小堆棧大小PTHREAD_STACK_MIN。設(shè)置線程的stacksize屬性的函數(shù)聲明如下:int
pthread_attr_setstacksize(pthread_attr_t*attr,size_t
stacksize);/*用于設(shè)置已初始化屬性對(duì)象attr
中的stacksize
屬性*/獲取線程stacksize屬性的函數(shù)聲明如下:int
pthread_attr_getstacksize(pthread_attr_t*_attr,size_t*stacksize);/*獲取stacksize屬性*/9.設(shè)置/獲取guardsize屬性guardsize
屬性用來警戒堆棧的大小。設(shè)置線程的guardsize屬性的函數(shù)聲明如下:int
pthread_attr_setguardsize(pthread_attr_t*attr,size_t
guardsize);/*用于設(shè)置已初始化屬性對(duì)象attr
中的guardsize
屬性*/獲取線程guardsize屬性的函數(shù)聲明如下:int
pthread_attr_getguardsize(pthread_attr_t*_attr,size_t*guardsize);/*獲取guardsize屬性*/【例10-2】線程屬性。設(shè)計(jì)步驟[1]在Vim中創(chuàng)建一個(gè)新工程文件,命名為“example10_2.c”。[2]在“example10_2.c”中創(chuàng)建代碼如下所示。#include<stdio.h>#include<pthread.h>#include<sched.h>voidmyfunction(){inti;for(i=0;i<3;i++)printf("Thisisapthread.\n");}voidmain(){pthread_attr_t
attr;pthread_t
tid;struct
sched_param
param;int
newprio=20;pthread_attr_init(&attr);pthread_attr_getschedparam(&attr,¶m);param.sched_priority=newprio;pthread_attr_setschedparam(&attr,¶m);pthread_create(&tid,&attr,(void*)myfunction,NULL);printf("Success.\n");}
[3]用GCC編譯并運(yùn)行程序,其結(jié)果如圖10-2所示。圖10-2設(shè)置線程屬性10.4線程等待終止1、線程等待線程的等待通過函數(shù)pthread_join()來實(shí)現(xiàn),該函數(shù)定義為:#include<pthread.h>int
pthread_join(pthread_t_th,void**_thread_return)該函數(shù)用來等待一個(gè)線程的結(jié)束,第1個(gè)參數(shù)為被等待的線程標(biāo)識(shí)符,第2個(gè)參數(shù)為一個(gè)用戶定義的指針,它可以用來存儲(chǔ)被等待線程的返回值。這個(gè)函數(shù)是一個(gè)線程阻塞函數(shù),調(diào)用它的函數(shù)將一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時(shí),處于等待狀態(tài)的線程資源被收回。2、線程終止新創(chuàng)建的線程從執(zhí)行用戶定義的函數(shù)處開始執(zhí)行,直到出現(xiàn)以下情況時(shí)退出:(1)執(zhí)行完成后隱式退出;(2)由線程本身顯示調(diào)用pthread_exit()函數(shù)退出;它的定義如下:#include<pthread.h>voidpthread_exit(void*retval);函數(shù)pthread_exit()終止調(diào)用的線程。參數(shù)retval的值對(duì)pthread_join()函數(shù)的成功有實(shí)際意義。然而,pthread_exit()的retval必須指定,在線程退出時(shí)它才退出的數(shù)據(jù),因此它不能夠作為正在退出的線程的自動(dòng)局部數(shù)據(jù)被分配。函數(shù)pthread_exit在成功調(diào)用時(shí)返回0,失敗時(shí)返回-1。10.5私有數(shù)據(jù)
在多線程環(huán)境下,進(jìn)程內(nèi)的所有線程共享進(jìn)程的數(shù)據(jù)空間,因此全局變量為所有線程共享,在程序設(shè)計(jì)中有時(shí)候需要保存線程自己的全局變量,這種特殊的變量僅在某個(gè)線程內(nèi)部有效。線程私有數(shù)據(jù)采用了一種被稱為一鍵多值的技術(shù),即一個(gè)鍵對(duì)應(yīng)多個(gè)值。訪問數(shù)據(jù)時(shí)都是通過鍵值來訪問,好像是對(duì)一個(gè)鍵值訪問。操作線程私有數(shù)據(jù)的函數(shù)主要有四個(gè):pthread_key_create()(創(chuàng)建一個(gè)鍵),pthread_setspecific()(為一個(gè)鍵設(shè)置線程私有數(shù)據(jù)),pthread_getspecific()(從一個(gè)鍵讀取線程私有數(shù)據(jù)),phread_key_delete()(刪除一個(gè)鍵)。這些函數(shù)的聲明如下:#include<pthread.h>int
pthread_key_create(pthread_key_t*key,void(*destr_function)(void*));int
pthread_setspecific(pthread_key_t
key,constvoid*pointer);void*pthread_getspecific(pthread_key_tkey);int
phread_key_delete(pthread_key_tkey);
pthread_key_create():從Linux的TSD池中分配一項(xiàng),將其值賦給key供以后訪問使用,它的第1個(gè)參數(shù)key為指向鍵值的指針,第2個(gè)參數(shù)為一個(gè)函數(shù)指針,如果指針不為空,則在線程退出時(shí)將以key所關(guān)聯(lián)的數(shù)據(jù)為參數(shù)調(diào)用destr_function(),釋放分配的緩沖區(qū)。
pthread_setspecific():該函數(shù)將pointer的值與key相關(guān)聯(lián)。用pthread_setspecific為一個(gè)鍵指定新的線程數(shù)據(jù)時(shí),線程必須先釋放原有的線程數(shù)據(jù)以回收空間。
pthread_getspecific():通過該函數(shù)得到與key相關(guān)聯(lián)的數(shù)據(jù)。
phread_key_delete():該函數(shù)用來刪除一個(gè)鍵,刪除后,鍵所占用的內(nèi)存將被釋放。需要注意的是,鍵占用的內(nèi)存被釋放,與該鍵關(guān)聯(lián)的線程數(shù)據(jù)所占用的內(nèi)存并不被釋放。因此,線程數(shù)據(jù)的釋放必須在釋放鍵之前完成。10.6線程同步如果所涉及的線程是獨(dú)立的,而且異步執(zhí)行,也就是說每個(gè)線程都包含了運(yùn)行時(shí)自身所需要的數(shù)據(jù)或方法,而不需要外部的資源或方法,也不必關(guān)心其他線程的狀態(tài)或行為。但是,有時(shí)候在進(jìn)行多線程的程序設(shè)計(jì)中需要實(shí)現(xiàn)多個(gè)線程共享同一段代碼。這時(shí),由于線程和線程之間互相競爭CPU資源,為了解決這個(gè)問題,必須要引入同步機(jī)制。在Linux系統(tǒng)中提供了多種處理線程同步問題的方式,其中最常用的有互斥鎖、條件變量和信號(hào)量?;コ怄i通過鎖機(jī)制來實(shí)現(xiàn)線程間的同步,互斥鎖從本質(zhì)上說就是一把鎖,提供對(duì)共享資源的保護(hù)訪問?;コ怄i具有以下三個(gè)特性:原子性:如果一個(gè)線程鎖定了一個(gè)互斥量,那么臨界區(qū)內(nèi)的操作要么全部完成,要么一個(gè)也不執(zhí)行。唯一性:如果一個(gè)線程鎖定了一個(gè)互斥量,那么在它解除鎖定之前,沒有其他線程可以鎖定這個(gè)互斥量。非繁忙等待:如果一個(gè)線程已經(jīng)鎖定了一個(gè)互斥量,第二個(gè)線程又試圖去鎖定這個(gè)互斥量,則第二個(gè)線程將被掛起(不占用任何CPU資源),直到第一個(gè)線程解除對(duì)這個(gè)互斥量的鎖定為止。第二個(gè)線程被喚醒并繼續(xù)執(zhí)行,同時(shí)鎖定這個(gè)互斥量。10.6.1互斥鎖表10-2互斥鎖函數(shù)函數(shù)功能pthread_mutex_init初始化一個(gè)互斥鎖pthread_mutex_destroy注銷一個(gè)互斥鎖pthread_mutex_lock加鎖,如果不成功,阻塞等待pthread_mutex_unlock解鎖pthread_mutex_trylock測試加鎖,如果不成功則立即返回1.初始化在Linux下,線程的互斥鎖在使用前,要對(duì)它進(jìn)行初始化。對(duì)于靜態(tài)分配的互斥鎖,可以把它設(shè)置為PTHREAD_MUTEX_INITIALIZER,或者調(diào)用pthread_mutex_init()。操作語句如下:pthread_mutex_t
mutex=PTHREAD_MUTEX_INITIALIZER;對(duì)于動(dòng)態(tài)分配的互斥量,在申請(qǐng)內(nèi)存(malloc)之后,通過pthread_mutex_init()進(jìn)行初始化,并且在釋放內(nèi)存(free)前需要調(diào)用pthread_mutex_destroy()來注銷互斥鎖。操作語句如下:int
pthread_mutex_init(pthread_mutex_t*restrictmutex,constpthread_mutexattr_t*restric
attr);int
pthread_mutex_destroy(pthread_mutex_t*mutex);返回值:成功則返回0,出錯(cuò)則返回錯(cuò)誤編號(hào)。表10-3互斥鎖的屬性
屬性值意義PTHREAD_MUTEX_TIMED_NP普通鎖,當(dāng)一個(gè)線程加鎖后,其余請(qǐng)求鎖的線程形成等待隊(duì)列,解鎖后按優(yōu)先級(jí)獲得鎖PTHREAD_MUTEX_RECURSIVE_NP嵌套鎖,允許一個(gè)線程對(duì)同一個(gè)鎖多次加鎖,并通過多次unlock解鎖,如果不是同線程請(qǐng)求,則在解鎖時(shí)重新競爭PTHREAD_MUTEX_ERRORCHECK_NP檢錯(cuò)鎖,解鎖后重新競爭PTHREAD_MUTEX_ADAPTIVE_NP適應(yīng)鎖2.互斥操作初始化以后就可以對(duì)互斥鎖進(jìn)行加鎖,如果互斥鎖已經(jīng)上了鎖,調(diào)用線程會(huì)阻塞,直到被解鎖。首先說一下加鎖函數(shù)。原型:int
pthread_mutex_lock(pthread_mutex_t*mutex);int
pthread_mutex_trylock
(pthread_mutex_t*mutex);返回值:成功則返回0,出錯(cuò)則返回錯(cuò)誤編號(hào)。其中pthread_mutex_trylock()函數(shù)是非阻塞調(diào)用模式,如果互斥鎖沒被鎖住,pthread_mutex_trylock()函數(shù)將進(jìn)行加鎖,并獲得對(duì)共享資源的訪問權(quán)限;如果互斥鎖被鎖住了,pthread_mutex_trylock()函數(shù)將不會(huì)阻塞等待而直接返回忙狀態(tài)。解鎖函數(shù)pthread_mutex_unlock():原型:
int
pthread_mutex_unlock
(pthread_mutex_t*mutex);返回值:成功則返回0,出錯(cuò)則返回錯(cuò)誤編號(hào)。3.死鎖死鎖主要發(fā)生在有多個(gè)依賴鎖存在時(shí),會(huì)在一個(gè)線程試圖以與另一個(gè)線程相反順序鎖住互斥鎖時(shí)發(fā)生。死鎖是使用互斥鎖時(shí)應(yīng)該盡量避免的,pthread庫可以跟蹤這種情形,最后一個(gè)線程試圖調(diào)用pthread_mutex_lock()時(shí)會(huì)失敗,并返回類型為EDEADLK的錯(cuò)誤?!纠?0-3】互斥鎖。設(shè)計(jì)步驟[1]在Vim中創(chuàng)建一個(gè)新工程文件,命名為“example10_3.c”。[2]在“example10_3.c”中創(chuàng)建代碼如下所示。
#include<stdio.h>#include<pthread.h>#include<sched.h>voidreader_function(void);voidwriter_function(void);charbuffer;int
buffer_has_item=0;pthread_mutex_t
mutex;main(void){pthread_treader;//delay.tv_nec=0;/*用默認(rèn)屬性初始化一個(gè)互斥鎖對(duì)象*/pthread_mutex_init(&mutex,NULL);pthread_create(&reader,NULL,(void*)reader_function,NULL);writer_function();}voidwriter_function(void){printf("writer_functionbegin.\n",buffer);inti=0;while(1){/*鎖定互斥鎖*/pthread_mutex_lock(&mutex);if(buffer_has_item==0){buffer='a'+i;i=i+1;buffer_has_item=1;}/*打開互斥鎖*/pthread_mutex_unlock(&mutex);sleep(1);}}voidreader_function(void){printf("reader_functionbegin.\n",buffer);while(1){pthread_mutex_lock(&mutex);if(buffer_has_item==1){printf("buffer=%c\n",buffer);buffer_has_item=0;}pthread_mutex_unlock(&mutex);sleep(1);}}使用GCC編譯并運(yùn)行程序,結(jié)果如圖10-3所示。圖10-3互斥鎖操作結(jié)果
互斥鎖一個(gè)明顯的缺點(diǎn)是它只有兩種狀態(tài):鎖定和非鎖定。而條件變量通過允許線程阻塞和等待另一個(gè)線程發(fā)送信號(hào)的方法彌補(bǔ)了互斥鎖的不足,它常和互斥鎖一起使用。使用時(shí),條件變量被用來阻塞一個(gè)線程,當(dāng)條件不滿足時(shí),線程往往解開相應(yīng)的互斥鎖并等待條件發(fā)生變化。一旦其它的某個(gè)線程改變了條件變量,它將通知相應(yīng)的條件變量喚醒一個(gè)或多個(gè)正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測試條件是否滿足。10.6.2條件變量表10-4對(duì)條件變量進(jìn)行操作的函數(shù)函數(shù)功能Pthread_cond_init()初始化條件變量Pthread_cond_wait()基于條件變量阻塞,無條件等待Pthread_cond_timedwait()阻塞直到指定事件發(fā)生,計(jì)時(shí)等待Pthread_cond_signal()解除特定線程的阻塞,存在多個(gè)扥帶線程時(shí)按入隊(duì)順序激活其中一個(gè)Pthread_cond_broadcast()解除所有線程的阻塞Pthread_cond_destroy()清除條件變量條件變量采用的數(shù)據(jù)類型是pthread_cond_t,在使用之前必須要進(jìn)行初始化。條件變量和互斥鎖一樣,也有靜態(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ù),其原型為:int
pthread_cond_init(pthread_cond_t*cond,pthread_condattr_t*cond_attr)其中cond是一個(gè)指向結(jié)構(gòu)pthread_cond_t的指針,cond_attr是一個(gè)指向結(jié)構(gòu)pthread_condattr_t的指針。結(jié)構(gòu)pthread_condattr_t是條件變量的屬性結(jié)構(gòu),和互斥鎖一樣可以用它來設(shè)置條件變量是進(jìn)程內(nèi)可用還是進(jìn)程間可用,默認(rèn)值是PTHREAD_PROCESS_PRIVATE,即此條件變量被同一進(jìn)程內(nèi)的各個(gè)線程使用。注意初始化條件變量只有未被使用時(shí)才能重新初始化或被釋放。函數(shù)pthread_cond_wait()使線程阻塞在一個(gè)條件變量上。它的函數(shù)原型為:int
pthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*__mutex);線程解開mutex指向的鎖并被條件變量cond阻塞,直到條件被信號(hào)喚醒。通常條件表達(dá)式在互斥鎖的保護(hù)下求值,如果條件表達(dá)式為假,線程基于條件表達(dá)式阻塞。當(dāng)一個(gè)線程改變條件變量值時(shí),條件變量獲得一個(gè)信號(hào),使得等待條件變量的線程退出等待狀態(tài)。另一個(gè)用來阻塞線程的函數(shù)是pthread_cond_timedwait(),它的原型為:int
pthread_cond_timedwait(pthread_cond_t*cond,pthread_mutex_t*mutex,conststruct
timespec*abstime);它比函數(shù)pthread_cond_wait()多了一個(gè)時(shí)間參數(shù),經(jīng)歷abstime段時(shí)間后,即使條件變量不滿足,阻塞也被解除。線程可以被函數(shù)pthread_cond_signal()和函數(shù)pthread_cond_broadcast()喚醒,但是要注意的是,條件變量只是起阻塞和喚醒線程的作用,具體的判斷條件還需用戶給出。線程被喚醒后,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來線程應(yīng)該仍阻塞在這里,被等待被下一次喚醒。這個(gè)過程一般用while語句實(shí)現(xiàn)。函數(shù)pthread_cond_signal()的原型為:int
pthread_cond_signal(pthread_cond_t*cond);它用來釋放被阻塞在條件變量cond上的一個(gè)線程。多個(gè)線程阻塞在此條件變量上時(shí),哪一個(gè)線程被喚醒是由線程的調(diào)度策略所決定的。函數(shù)pthread_cond_broadcast()激活所有等待線程,其函數(shù)原型為:int
pthread_cond_broadcast(pthread_cond_t*cond);當(dāng)一個(gè)條件變量不再使用時(shí),需要將其清除,清除一個(gè)條件變量使用函數(shù)pthread_cond_destroy()來實(shí)現(xiàn),其函數(shù)原型為:int
pthread_cond_destroy(pthread_cond_t*cond);【例10-4】條件變量。設(shè)計(jì)步驟[1]在Vim中創(chuàng)建一個(gè)新工程文件,命名為“example10_4.c”。[2]在“example10_4.c”中創(chuàng)建代碼如下所示。#include<stdlib.h>#include<stdio.h>#include<pthread.h>#include<sched.h>pthread_mutex_t
count_lock;pthread_cond_t
count_nonzero;unsignedcount;decrement_count(){printf("Createdecrement_count
pthreadsuccess!\n");pthread_mutex_lock(&count_lock);while(count==0)pthread_cond_wait(&count_nonzero,&count_lock);count=count-1;printf("Countisdecrement:count=%d\n",count);pthread_mutex_unlock(&count_lock);}increment_count(){printf("Createincrement_count
pthreadsuccess!\n");pthread_mutex_lock(&count_lock);if(count==0)pthread_cond_signal(&count_nonzero);count=count+1;printf("Countisincrement:count=%d\n",count);pthread_mutex_unlock(&count_lock);}intmain(){pthread_tid;int
i,ret;ret=pthread_create(&id,NULL,(void*)decrement_count,NULL);if(ret!=0){printf("Createdecrement_count
pthreaderror!\n");exit(1);}ret=pthread_create(&id,NULL,(void*)increment_count,NULL);if(ret!=0){printf("Createincrement_count
pthreaderror!\n");exit(1);}}用GCC編譯運(yùn)行程序結(jié)果如圖10-4所示。圖10-4條件變量例10-4的運(yùn)行結(jié)果1965年,E.W.Dijkstra提出了信號(hào)量的概念,之后信號(hào)量成為操作系統(tǒng)實(shí)現(xiàn)互斥和同步的一種普遍機(jī)制。信號(hào)量是一種特殊的變量,只能取正整數(shù)值,對(duì)這些正整數(shù)只能采取兩種操作,P操作(代表等待、關(guān)操作)、V操作(代表信號(hào)、開操作)。定義如下:P(sem):如果sem的值大于0,則sem減1,如果sem的值為0,則掛起該線程。V(sem):如果有其他進(jìn)程因等待sem而被掛起,則讓它恢復(fù)執(zhí)行,如果沒有線程等待sem而被掛起,則sem加上1。10.6.3信號(hào)量1、創(chuàng)建信號(hào)量在使用信號(hào)量之前,首先需要?jiǎng)?chuàng)建一個(gè)信號(hào)量。創(chuàng)建一個(gè)信號(hào)量的函數(shù)為semget(),其函數(shù)聲明如下:#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>int
semget(key_t,int
nsems,intflag);該函數(shù)用來打開一個(gè)新的信號(hào)量集合,或者打開一個(gè)已經(jīng)存在的信號(hào)量集合。其中,參數(shù)key表示所創(chuàng)建或打開的信號(hào)量集的鍵;參數(shù)nsems表示創(chuàng)建的信號(hào)量集中信號(hào)量的個(gè)數(shù),此參數(shù)只在創(chuàng)建一個(gè)新的信號(hào)量集時(shí)才有效;參數(shù)flag表示調(diào)用函數(shù)的操作類型,也可以用來設(shè)置信號(hào)量集的訪問權(quán)限。調(diào)用函數(shù)semget()的作用由參數(shù)key和flag決定,函數(shù)調(diào)用成功時(shí)返回值為信號(hào)量的引用標(biāo)識(shí)符,調(diào)用失敗時(shí),返回-1。2、對(duì)信號(hào)量的操作對(duì)信號(hào)量的操作使用如下函數(shù):#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>int
semop(int
semid,struct
sembuf
semoparray[],size_t
nops);參數(shù)semid是信號(hào)量集的引用id,semoparray[]是一個(gè)sembuf類型數(shù)組,sembuf結(jié)構(gòu)用于指定調(diào)用semop()函數(shù)所作的操作,數(shù)組semoparray[]中的元素的個(gè)數(shù)由nops決定。sembuf結(jié)構(gòu)如下:struct
sembuf{
usbort
sem_num;shortsem_op;shortsem_flag;}其中,sem_num指定要操作的信號(hào)量;sem_flag為操作標(biāo)記;sem_op用于表示所執(zhí)行的操作,相應(yīng)取值的含義如下:sem_op>0:表示線程對(duì)資源使用完畢,交回該資源。此時(shí)信號(hào)量集的semid_ds結(jié)構(gòu)的sem_base.semval將加上sem_op的值。如果此時(shí)設(shè)置了SEM_UNDO位,則信號(hào)量的調(diào)整值將減去sem_op絕對(duì)值。sem_op=0:表示進(jìn)程要等待,直到sem_base.semval的值變?yōu)?。sem_op<0:表示進(jìn)程希望使用資源。此時(shí)將比較sem_base.semval和sem_op的絕對(duì)值的大小。3、對(duì)信號(hào)量的控制對(duì)信號(hào)量的控制操作是通過semctl()來山實(shí)現(xiàn)的,函數(shù)原型如下:#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>int
semctl(int
semid,int
semnum,int
cmd,unionsenum
arg);其中semid為信號(hào)量集的引用標(biāo)識(shí)符,semnum用于指定信號(hào)量集中某個(gè)特定的信號(hào)量,參數(shù)cmd表示調(diào)用該函數(shù)希望執(zhí)行的操作,參數(shù)arg是semun聯(lián)合。10.7出錯(cuò)處理在軟件開發(fā)時(shí)需要時(shí)刻檢查錯(cuò)誤發(fā)生的各種可能,例如創(chuàng)建進(jìn)程失敗、打開文件失敗等。當(dāng)錯(cuò)誤發(fā)生時(shí),程序應(yīng)當(dāng)給出提示信息并進(jìn)行相應(yīng)的處理。在調(diào)用庫函數(shù)或系統(tǒng)調(diào)用函數(shù)后,絕
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 《綿陽求職招聘技巧》課件
- 2020-2021學(xué)年遼寧省沈陽市郊聯(lián)體高一下學(xué)期期末考試歷史試題
- 小學(xué)一年級(jí)10以內(nèi)數(shù)字的分與合
- 小學(xué)數(shù)學(xué)新人教版一年級(jí)下冊20以內(nèi)口算練習(xí)題大全
- 小學(xué)三年級(jí)數(shù)學(xué)三位數(shù)加減法口算題
- 《汽車行業(yè)概述》課件
- 《運(yùn)輸與包裝》課件
- 吉他行業(yè)客服工作總結(jié)用心服務(wù)打造音樂快樂
- 《光纖通信基礎(chǔ)知識(shí)》課件
- 酒店招聘與人才引進(jìn)策略
- GB/T 9128.2-2023鋼制管法蘭用金屬環(huán)墊第2部分:Class系列
- 網(wǎng)絡(luò)經(jīng)濟(jì)學(xué)PPT完整全套教學(xué)課件
- 2023年主治醫(yī)師(中級(jí))-臨床醫(yī)學(xué)檢驗(yàn)學(xué)(中級(jí))代碼:352考試參考題庫附帶答案
- 機(jī)械原理課程設(shè)計(jì)鎖梁自動(dòng)成型機(jī)床切削機(jī)構(gòu)
- 順產(chǎn)臨床路徑
- 人教版培智一年級(jí)上生活適應(yīng)教案
- 推動(dòng)架機(jī)械加工工序卡片
- RoHS檢測報(bào)告完整版
- 中國近現(xiàn)代史綱要(上海建橋?qū)W院)智慧樹知到答案章節(jié)測試2023年
- 同濟(jì)大學(xué)土力學(xué)試卷2023
- 南理工2023運(yùn)籌學(xué)試卷A及答案
評(píng)論
0/150
提交評(píng)論