Linux 第10章 線程控制_第1頁
Linux 第10章 線程控制_第2頁
Linux 第10章 線程控制_第3頁
Linux 第10章 線程控制_第4頁
Linux 第10章 線程控制_第5頁
已閱讀5頁,還剩79頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第10章線程控制

計算機在運行時其執(zhí)行過程從大到小可以分為作業(yè)、進程和線程3個級別。線程是系統(tǒng)分配處理器時間資源的基本單元,或者說是進程之內(nèi)獨立執(zhí)行的一個單元。對于操作系統(tǒng)而言,其調(diào)度單元是線程。Linux中的線程是輕量級線程(lightweightthread),Linux中的線程調(diào)度是由內(nèi)核調(diào)度程序完成的,每個線程都有自己的ID號。與進程相比,線程所消耗的系統(tǒng)資源較少、創(chuàng)建較快、相互間的通信也比較容易。

Linux線程10.1創(chuàng)建線程

10.2線程屬性

10.3線程等待終止10.4私有數(shù)據(jù)10.5線程同步10.6出錯處理10.7思考與練習(xí)

10.810.1Linux線程線程是在共享內(nèi)存空間中并發(fā)的多道執(zhí)行路徑,它們共享一個進程的資源。在兩個普通進程(非線程)間進行切換時,內(nèi)核準備從一個進程的上下文切換到另一個進程的上下文要有很大的花費,包括保存老進程CPU狀態(tài),并加載新進程的保存狀態(tài),用新進程的內(nèi)存映像替換老進程的內(nèi)存映像。線程也有其私有數(shù)據(jù)信息,包括:線程號(threadID):每個線程都有一個唯一的線程號與之對應(yīng)。寄存器(包括程序計數(shù)器和堆棧指針)。堆棧信號掩碼優(yōu)先級線程私有的存儲空間。多線程具有以下的優(yōu)點:(1)提高應(yīng)用程序的響應(yīng)速度。將耗時長的操作置于一個新的線程,可以避免系統(tǒng)的等待。(2)使多CPU系統(tǒng)更有效。操作系統(tǒng)會保證當(dāng)線程數(shù)目不大于CPU數(shù)目時不同的線程運行于不同的CPU上。(3)改善程序結(jié)構(gòu)。一個長而復(fù)雜的進程可以分為多個線程,成為獨立運行的部分。10.1.1線程和進程的關(guān)系Linux是支持多線程的,在一個進程內(nèi)生成多個線程。一個進程可以擁有一個或多個線程。線程和進程二者之間的關(guān)系有以下幾點。(1)首先,線程采用了多個線程可共享資源的設(shè)計思想。在多進程情況下,每個進程都有自己獨立的地址空間,在多線程情況下,同一進程內(nèi)的線程共享進程的地址空間。線程和進程的最大區(qū)別在于線程完全共享相同的地址空間,運行在同一地址上。(2)其次,由于進程地址空間獨立而線程共享地址空間,所以從一個線程切換到另一線程所花費的代價比進程低。(3)再次,進程本身的信息在內(nèi)存中占用的空間比線程大。因此,線程更能充分地利用內(nèi)存。線程可以看作是在進程內(nèi)部執(zhí)行的指定序列。(4)最后,線程間的通信比進程間的通信更加方便和省時。進程間的數(shù)據(jù)空間相互獨立,彼此通信要以專門的通信方式進行,通信時必須經(jīng)過操作系統(tǒng),而同一進程的多個線程共享數(shù)據(jù)空間,一個線程的數(shù)據(jù)可以直接提供給其他線程使用,不必進過操作系統(tǒng)。10.1.2線程分類內(nèi)核線程Linux內(nèi)核可以看作一個服務(wù)進程(管理軟硬件資源,響應(yīng)用戶進程的種種合理以及不合理的請求)。內(nèi)核需要多個執(zhí)行流并行,為了防止可能的阻塞,多線程化是必要的。內(nèi)核線程就是內(nèi)核的分身,一個分身可以處理一件特定事情。Linux內(nèi)核使用內(nèi)核線程來將內(nèi)核分成幾個功能模塊,像kswapd、kflushd等,這在處理異步事件如異步IO時特別有用。內(nèi)核線程的使用是廉價的,唯一使用的資源就是內(nèi)核棧和上下文切換時保存寄存器的空間。支持多線程的內(nèi)核叫做多線程內(nèi)核(Multi-Threadskernel)。內(nèi)核線程的調(diào)度由內(nèi)核負責(zé),一個內(nèi)核線程處于阻塞狀態(tài)時不影響其他的內(nèi)核線程,因為其是調(diào)度的基本單位。這與用戶線程是不一樣的。用戶線程用戶線程在用戶空間中實現(xiàn),允許多線程的程序運行時不需要特定的內(nèi)核支持,內(nèi)核不需要直接對用戶線程進程調(diào)度,內(nèi)核的調(diào)度對象和傳統(tǒng)進程一樣,還是進程本身,內(nèi)核并不知道用戶線程的存在。用戶線程的優(yōu)點如下:(1)某些線程操作的系統(tǒng)消耗大大減少。比如,對屬于同一個進程的線程之間進行調(diào)度切換時,不需要調(diào)用系統(tǒng)調(diào)用,因此將減少額外的消耗,一個進程往往可以啟動上千個線程。(2)用戶線程的實現(xiàn)方式可以被定制或修改,以適應(yīng)特殊應(yīng)用的要求。它對于多媒體實時過程等尤其有用。另外,用戶線程可以比內(nèi)核線程實現(xiàn)方法默認情況支持更多的線程。

系統(tǒng)創(chuàng)建線程的順序如下:當(dāng)一個進程啟動后,它會自動創(chuàng)建一個線程即主線程(mainthread)或者初始化線程(initialthread),然后利用pthread_initialize()初始化系統(tǒng)管理線程并且啟動線程機制。線程機制啟動以后需要創(chuàng)建線程,需要phread_create()向管理線程發(fā)送REQ_CREATE請求,調(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)建線程,并為其分配一個唯一的標識符pthread_t。調(diào)用者提供一個將由該線程執(zhí)行的函數(shù),該調(diào)用還可以為線程顯式指定一些屬性。表10-1創(chuàng)建線程其他系統(tǒng)函數(shù)函數(shù)說明pthread_t

pthread_self(void)獲取本線程的線程IDint

pthread_equal(pthread_tthread1,pthread_tthread2)判斷兩個線程ID是否指向同一線程int

pthread_once(pthread_once_t*once_control,void(*init_routine)(void))用來保證init_routine線程函數(shù)在進程中僅執(zhí)行一次。【例10-1】創(chuàng)建線程。設(shè)計步驟[1]在Vim中創(chuàng)建一個新工程文件,命名為“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編譯運行程序結(jié)果如圖10-1所示。圖10-1創(chuàng)建線程10.3線程屬性

創(chuàng)建線程函數(shù)pthread_create()的第二個參數(shù)用來指定線程的屬性。線程屬性主要有綁定屬性、分離屬性、堆棧地址、堆棧大小、優(yōu)先級等。其中系統(tǒng)默認的是非邦定、非分離、缺省1M的堆棧、與父進程同樣級別的優(yōu)先級。(1)綁定屬性。在Linux中,采用的是“一對一”的線程機制。也就是一個用戶線程對應(yīng)一個內(nèi)核線程。綁定屬性就是指一個用戶線程固定地分配給一個內(nèi)核線程,因為CPU時間片的調(diào)度是面向內(nèi)核線程(輕量級進程)的,因此具有綁定屬性的線程可以保證在需要的時候總有一個內(nèi)核線程與之對應(yīng),而與之對應(yīng)的非綁定屬性就是指用戶線程和內(nèi)核線程的關(guān)系不是始終固定的,而是由系統(tǒng)來分配。(2)分離屬性。分離屬性是決定線程以一個什么樣的方式來終止自己。在非分離情況下,當(dāng)一個線程結(jié)束時,它多占用的線程沒有得到釋放,也就是沒有真正的終止,需要通過pthread_join來釋放資源。而在分離屬性情況下,一個線程結(jié)束時會立即釋放它所占有的系統(tǒng)資源。但這里有一點要注意的是,如果設(shè)置一個線程分離屬性,而這個線程又運行得非??斓脑挘敲此芸赡茉趐thread_create函數(shù)返回之前就終止了線程函數(shù)的運行,它終止以后就很有可能將線程號和系統(tǒng)資源移交給其他的線程使用,這時調(diào)用pthread_create()的線程就得到錯誤的線程號。1.設(shè)置/獲取detachstate

屬性detachstate

屬性表示新創(chuàng)建的線程與進程中其它線程是處于分離狀態(tài)還是可連接狀態(tài)。其合法值包括:PTHREAD_CREATE_DETACHED:此選項使得使用attr

創(chuàng)建的所有線程處于分離狀態(tài)。線程終止時,系統(tǒng)將自動回收與帶有此狀態(tài)的線程相關(guān)聯(lián)的資源。調(diào)用使用此屬性創(chuàng)建的pthread_detach()或pthread_join()函數(shù)將導(dǎo)致錯誤。PTHREAD_CREATE_JOINABLE:此選項使得使用attr

創(chuàng)建的所有線程處于可連接狀態(tài)。線程終止時,不會回收與帶有此狀態(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è)置已初始化屬性對象attr

中的detachstate

屬性*/獲取線程detachstate屬性的函數(shù)聲明如下:int

pthread_attr_getdetachstate(pthread_attr_t*attr,int*detachstate);/*獲取detachstate

屬性*/2.設(shè)置/獲取guardsize

屬性guardsize

屬性允許應(yīng)用程序指定使用此屬性對象創(chuàng)建的線程的守護區(qū)大小。所指定的守護區(qū)大小的單位為字節(jié)。大多數(shù)系統(tǒng)將守護區(qū)大小向上舍入為系統(tǒng)可配置變量PAGESIZE的倍數(shù)。如果指定了零值,則不會創(chuàng)建守護區(qū)。為應(yīng)用程序提供了guardsize

屬性的作用為:溢出保護可能會導(dǎo)致系統(tǒng)資源浪費。如果應(yīng)用程序創(chuàng)建大量線程,并且已知這些線程永遠不會溢出其棧,則可以關(guān)閉溢出保護區(qū)。通過關(guān)閉溢出保護區(qū),可以節(jié)省系統(tǒng)資源。l線程在棧上分配大型數(shù)據(jù)結(jié)構(gòu)時,可能需要較大的溢出保護區(qū)來檢測棧溢出。設(shè)置線程的guardsize屬性的函數(shù)聲明如下:int

pthread_attr_setguardsize(pthread_attr_t*attr,size_t

guardsize);/*用于設(shè)置已初始化屬性對象attr中的guardsize屬性值*/獲取線程guardsize屬性的函數(shù)聲明如下:int

pthread_attr_getguardsize(pthread_attr_t*attr,size_t*guardsize);/*獲取guardsize屬性*/3.設(shè)置/獲取schedparam

屬性schedparam

屬性的合法值因調(diào)度策略的不同而異。對于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è)置已初始化屬性對象attr

中的schedparam

屬性*/獲取線程schedparam屬性的函數(shù)聲明如下:int

pthread_attr_getschedparam(pthread_attr_t*attr,struct

sched_param*schedparam);/*獲取schedparam屬性*/4.設(shè)置/獲取schedpolicy

屬性schedpolicy

屬性允許通過此屬性對象創(chuàng)建的線程使用特定的調(diào)度策略,包括SCHED_OTHER(正常、非實時)、SCHED_RR(實時、輪轉(zhuǎn)法)和SCHED_FIFO(實時、先入先出)等3種,缺省為SCHED_OTHER。設(shè)置線程的schedpolicy屬性的函數(shù)聲明如下:int

pthread_attr_setschedpolicy(pthread_attr_t*attr,intpolicy);/*用于設(shè)置已初始化屬性對象attr

中的schedpolicy

屬性*/獲取線程schedpolicy屬性的函數(shù)聲明如下:int

pthread_attr_getschedpolicy(pthread_attr_t*attr,int*policy);/*獲取schedpolicy

屬性*/5.設(shè)置/獲取inheritsched

屬性inheritsched

屬性選用是從創(chuàng)建線程中繼承還是從此屬性對象中獲得調(diào)度策略及關(guān)聯(lián)屬性。inheritsched

有兩種值可供選擇:PTHREAD_INHERIT_SCHED:此選項指定從創(chuàng)建線程中繼承調(diào)度策略及關(guān)聯(lián)屬性。如果使用attr

創(chuàng)建了線程,將忽略attr

參數(shù)中的調(diào)度策略和關(guān)聯(lián)屬性。PTHREAD_EXPLICIT_SCHED:此選項指定,從此屬性對象中獲得已創(chuàng)建線程的調(diào)度策略及關(guān)聯(lián)屬性。設(shè)置線程的inheritsched屬性的函數(shù)聲明如下:int

pthread_attr_setinheritsched(pthread_attr_t*attr,intinherit);/*用于設(shè)置已初始化屬性對象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)度實體。PTHREAD_SCOPE_PROCESS:使用此競爭范圍創(chuàng)建的線程,將直接與其進程內(nèi)使用此調(diào)度競爭范圍創(chuàng)建的其他線程爭用資源。此屬性一般用于表示不應(yīng)綁定用戶線程(不綁定到任何特定的內(nèi)核調(diào)度的實體)。設(shè)置線程的scope屬性的函數(shù)聲明如下:int

pthread_attr_setscope(pthread_attr_t*attr,intscope);/*用于設(shè)置已初始化屬性對象attr

中的contentionscope

屬性*/獲取線程scope屬性的函數(shù)聲明如下:int

pthread_attr_getscope(pthread_attr_t*attr,int*scope); /*獲取scope屬性*/7.設(shè)置/獲取stackaddr屬性此屬性選項指定創(chuàng)建的線程將要使用的堆棧地址。應(yīng)用程序全面負責(zé)這些堆棧的分配、管理和取消分配。存儲分配的選項為malloc(3C)、brk(2)和mmap(2)函數(shù)。如果使用此選項,則只能使用此屬性對象創(chuàng)建一個線程。如果創(chuàng)建了多個線程,它們將使用同一個堆棧。stackaddr

屬性的缺省值為NULL。如果線程用戶堆棧的存儲不是由庫分配的(也就是說,stackaddr

屬性不是NULL),將忽略guardsize

屬性。設(shè)置線程的stackaddr屬性的函數(shù)聲明如下:int

pthread_attr_setstackaddr(pthread_attr_t*attr,void*stackaddr);/*用于設(shè)置已初始化屬性對象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:此選項指定,使用此屬性對象創(chuàng)建的線程的用戶堆棧大小將使用缺省堆棧大小。此值為某個線程所需的最小堆棧大?。ㄒ宰止?jié)為單位)。對于所有線程來說,這個最小值可能無法接受。

stacksize:具體的大小。定義使用此屬性對象創(chuàng)建的線程的用戶堆棧大?。ㄒ宰止?jié)為單位)。此值必須大于或等于最小堆棧大小PTHREAD_STACK_MIN。設(shè)置線程的stacksize屬性的函數(shù)聲明如下:int

pthread_attr_setstacksize(pthread_attr_t*attr,size_t

stacksize);/*用于設(shè)置已初始化屬性對象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è)置已初始化屬性對象attr

中的guardsize

屬性*/獲取線程guardsize屬性的函數(shù)聲明如下:int

pthread_attr_getguardsize(pthread_attr_t*_attr,size_t*guardsize);/*獲取guardsize屬性*/【例10-2】線程屬性。設(shè)計步驟[1]在Vim中創(chuàng)建一個新工程文件,命名為“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編譯并運行程序,其結(jié)果如圖10-2所示。圖10-2設(shè)置線程屬性10.4線程等待終止1、線程等待線程的等待通過函數(shù)pthread_join()來實現(xiàn),該函數(shù)定義為:#include<pthread.h>int

pthread_join(pthread_t_th,void**_thread_return)該函數(shù)用來等待一個線程的結(jié)束,第1個參數(shù)為被等待的線程標識符,第2個參數(shù)為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。這個函數(shù)是一個線程阻塞函數(shù),調(diào)用它的函數(shù)將一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時,處于等待狀態(tài)的線程資源被收回。2、線程終止新創(chuàng)建的線程從執(zhí)行用戶定義的函數(shù)處開始執(zhí)行,直到出現(xiàn)以下情況時退出:(1)執(zhí)行完成后隱式退出;(2)由線程本身顯示調(diào)用pthread_exit()函數(shù)退出;它的定義如下:#include<pthread.h>voidpthread_exit(void*retval);函數(shù)pthread_exit()終止調(diào)用的線程。參數(shù)retval的值對pthread_join()函數(shù)的成功有實際意義。然而,pthread_exit()的retval必須指定,在線程退出時它才退出的數(shù)據(jù),因此它不能夠作為正在退出的線程的自動局部數(shù)據(jù)被分配。函數(shù)pthread_exit在成功調(diào)用時返回0,失敗時返回-1。10.5私有數(shù)據(jù)

在多線程環(huán)境下,進程內(nèi)的所有線程共享進程的數(shù)據(jù)空間,因此全局變量為所有線程共享,在程序設(shè)計中有時候需要保存線程自己的全局變量,這種特殊的變量僅在某個線程內(nèi)部有效。線程私有數(shù)據(jù)采用了一種被稱為一鍵多值的技術(shù),即一個鍵對應(yīng)多個值。訪問數(shù)據(jù)時都是通過鍵值來訪問,好像是對一個鍵值訪問。操作線程私有數(shù)據(jù)的函數(shù)主要有四個:pthread_key_create()(創(chuàng)建一個鍵),pthread_setspecific()(為一個鍵設(shè)置線程私有數(shù)據(jù)),pthread_getspecific()(從一個鍵讀取線程私有數(shù)據(jù)),phread_key_delete()(刪除一個鍵)。這些函數(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池中分配一項,將其值賦給key供以后訪問使用,它的第1個參數(shù)key為指向鍵值的指針,第2個參數(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為一個鍵指定新的線程數(shù)據(jù)時,線程必須先釋放原有的線程數(shù)據(jù)以回收空間。

pthread_getspecific():通過該函數(shù)得到與key相關(guān)聯(lián)的數(shù)據(jù)。

phread_key_delete():該函數(shù)用來刪除一個鍵,刪除后,鍵所占用的內(nèi)存將被釋放。需要注意的是,鍵占用的內(nèi)存被釋放,與該鍵關(guān)聯(lián)的線程數(shù)據(jù)所占用的內(nèi)存并不被釋放。因此,線程數(shù)據(jù)的釋放必須在釋放鍵之前完成。10.6線程同步如果所涉及的線程是獨立的,而且異步執(zhí)行,也就是說每個線程都包含了運行時自身所需要的數(shù)據(jù)或方法,而不需要外部的資源或方法,也不必關(guān)心其他線程的狀態(tài)或行為。但是,有時候在進行多線程的程序設(shè)計中需要實現(xiàn)多個線程共享同一段代碼。這時,由于線程和線程之間互相競爭CPU資源,為了解決這個問題,必須要引入同步機制。在Linux系統(tǒng)中提供了多種處理線程同步問題的方式,其中最常用的有互斥鎖、條件變量和信號量?;コ怄i通過鎖機制來實現(xiàn)線程間的同步,互斥鎖從本質(zhì)上說就是一把鎖,提供對共享資源的保護訪問?;コ怄i具有以下三個特性:原子性:如果一個線程鎖定了一個互斥量,那么臨界區(qū)內(nèi)的操作要么全部完成,要么一個也不執(zhí)行。唯一性:如果一個線程鎖定了一個互斥量,那么在它解除鎖定之前,沒有其他線程可以鎖定這個互斥量。非繁忙等待:如果一個線程已經(jīng)鎖定了一個互斥量,第二個線程又試圖去鎖定這個互斥量,則第二個線程將被掛起(不占用任何CPU資源),直到第一個線程解除對這個互斥量的鎖定為止。第二個線程被喚醒并繼續(xù)執(zhí)行,同時鎖定這個互斥量。10.6.1互斥鎖表10-2互斥鎖函數(shù)函數(shù)功能pthread_mutex_init初始化一個互斥鎖pthread_mutex_destroy注銷一個互斥鎖pthread_mutex_lock加鎖,如果不成功,阻塞等待pthread_mutex_unlock解鎖pthread_mutex_trylock測試加鎖,如果不成功則立即返回1.初始化在Linux下,線程的互斥鎖在使用前,要對它進行初始化。對于靜態(tài)分配的互斥鎖,可以把它設(shè)置為PTHREAD_MUTEX_INITIALIZER,或者調(diào)用pthread_mutex_init()。操作語句如下:pthread_mutex_t

mutex=PTHREAD_MUTEX_INITIALIZER;對于動態(tài)分配的互斥量,在申請內(nèi)存(malloc)之后,通過pthread_mutex_init()進行初始化,并且在釋放內(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,出錯則返回錯誤編號。表10-3互斥鎖的屬性

屬性值意義PTHREAD_MUTEX_TIMED_NP普通鎖,當(dāng)一個線程加鎖后,其余請求鎖的線程形成等待隊列,解鎖后按優(yōu)先級獲得鎖PTHREAD_MUTEX_RECURSIVE_NP嵌套鎖,允許一個線程對同一個鎖多次加鎖,并通過多次unlock解鎖,如果不是同線程請求,則在解鎖時重新競爭PTHREAD_MUTEX_ERRORCHECK_NP檢錯鎖,解鎖后重新競爭PTHREAD_MUTEX_ADAPTIVE_NP適應(yīng)鎖2.互斥操作初始化以后就可以對互斥鎖進行加鎖,如果互斥鎖已經(jīng)上了鎖,調(diào)用線程會阻塞,直到被解鎖。首先說一下加鎖函數(shù)。原型:int

pthread_mutex_lock(pthread_mutex_t*mutex);int

pthread_mutex_trylock

(pthread_mutex_t*mutex);返回值:成功則返回0,出錯則返回錯誤編號。其中pthread_mutex_trylock()函數(shù)是非阻塞調(diào)用模式,如果互斥鎖沒被鎖住,pthread_mutex_trylock()函數(shù)將進行加鎖,并獲得對共享資源的訪問權(quán)限;如果互斥鎖被鎖住了,pthread_mutex_trylock()函數(shù)將不會阻塞等待而直接返回忙狀態(tài)。解鎖函數(shù)pthread_mutex_unlock():原型:

int

pthread_mutex_unlock

(pthread_mutex_t*mutex);返回值:成功則返回0,出錯則返回錯誤編號。3.死鎖死鎖主要發(fā)生在有多個依賴鎖存在時,會在一個線程試圖以與另一個線程相反順序鎖住互斥鎖時發(fā)生。死鎖是使用互斥鎖時應(yīng)該盡量避免的,pthread庫可以跟蹤這種情形,最后一個線程試圖調(diào)用pthread_mutex_lock()時會失敗,并返回類型為EDEADLK的錯誤?!纠?0-3】互斥鎖。設(shè)計步驟[1]在Vim中創(chuàng)建一個新工程文件,命名為“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;/*用默認屬性初始化一個互斥鎖對象*/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編譯并運行程序,結(jié)果如圖10-3所示。圖10-3互斥鎖操作結(jié)果

互斥鎖一個明顯的缺點是它只有兩種狀態(tài):鎖定和非鎖定。而條件變量通過允許線程阻塞和等待另一個線程發(fā)送信號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變量被用來阻塞一個線程,當(dāng)條件不滿足時,線程往往解開相應(yīng)的互斥鎖并等待條件發(fā)生變化。一旦其它的某個線程改變了條件變量,它將通知相應(yīng)的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測試條件是否滿足。10.6.2條件變量表10-4對條件變量進行操作的函數(shù)函數(shù)功能Pthread_cond_init()初始化條件變量Pthread_cond_wait()基于條件變量阻塞,無條件等待Pthread_cond_timedwait()阻塞直到指定事件發(fā)生,計時等待Pthread_cond_signal()解除特定線程的阻塞,存在多個扥帶線程時按入隊順序激活其中一個Pthread_cond_broadcast()解除所有線程的阻塞Pthread_cond_destroy()清除條件變量條件變量采用的數(shù)據(jù)類型是pthread_cond_t,在使用之前必須要進行初始化。條件變量和互斥鎖一樣,也有靜態(tài)動態(tài)兩種創(chuàng)建方式,靜態(tài)方式使用PTHREAD_COND_INITIALIZER常量,如下:pthread_cond_t

cond=PTHREAD_COND_INITIALIZER;動態(tài)方式調(diào)用pthread_cond_init()函數(shù),其原型為:int

pthread_cond_init(pthread_cond_t*cond,pthread_condattr_t*cond_attr)其中cond是一個指向結(jié)構(gòu)pthread_cond_t的指針,cond_attr是一個指向結(jié)構(gòu)pthread_condattr_t的指針。結(jié)構(gòu)pthread_condattr_t是條件變量的屬性結(jié)構(gòu),和互斥鎖一樣可以用它來設(shè)置條件變量是進程內(nèi)可用還是進程間可用,默認值是PTHREAD_PROCESS_PRIVATE,即此條件變量被同一進程內(nèi)的各個線程使用。注意初始化條件變量只有未被使用時才能重新初始化或被釋放。函數(shù)pthread_cond_wait()使線程阻塞在一個條件變量上。它的函數(shù)原型為:int

pthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*__mutex);線程解開mutex指向的鎖并被條件變量cond阻塞,直到條件被信號喚醒。通常條件表達式在互斥鎖的保護下求值,如果條件表達式為假,線程基于條件表達式阻塞。當(dāng)一個線程改變條件變量值時,條件變量獲得一個信號,使得等待條件變量的線程退出等待狀態(tài)。另一個用來阻塞線程的函數(shù)是pthread_cond_timedwait(),它的原型為:int

pthread_cond_timedwait(pthread_cond_t*cond,pthread_mutex_t*mutex,conststruct

timespec*abstime);它比函數(shù)pthread_cond_wait()多了一個時間參數(shù),經(jīng)歷abstime段時間后,即使條件變量不滿足,阻塞也被解除。線程可以被函數(shù)pthread_cond_signal()和函數(shù)pthread_cond_broadcast()喚醒,但是要注意的是,條件變量只是起阻塞和喚醒線程的作用,具體的判斷條件還需用戶給出。線程被喚醒后,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來線程應(yīng)該仍阻塞在這里,被等待被下一次喚醒。這個過程一般用while語句實現(xiàn)。函數(shù)pthread_cond_signal()的原型為:int

pthread_cond_signal(pthread_cond_t*cond);它用來釋放被阻塞在條件變量cond上的一個線程。多個線程阻塞在此條件變量上時,哪一個線程被喚醒是由線程的調(diào)度策略所決定的。函數(shù)pthread_cond_broadcast()激活所有等待線程,其函數(shù)原型為:int

pthread_cond_broadcast(pthread_cond_t*cond);當(dāng)一個條件變量不再使用時,需要將其清除,清除一個條件變量使用函數(shù)pthread_cond_destroy()來實現(xiàn),其函數(shù)原型為:int

pthread_cond_destroy(pthread_cond_t*cond);【例10-4】條件變量。設(shè)計步驟[1]在Vim中創(chuàng)建一個新工程文件,命名為“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編譯運行程序結(jié)果如圖10-4所示。圖10-4條件變量例10-4的運行結(jié)果1965年,E.W.Dijkstra提出了信號量的概念,之后信號量成為操作系統(tǒng)實現(xiàn)互斥和同步的一種普遍機制。信號量是一種特殊的變量,只能取正整數(shù)值,對這些正整數(shù)只能采取兩種操作,P操作(代表等待、關(guān)操作)、V操作(代表信號、開操作)。定義如下:P(sem):如果sem的值大于0,則sem減1,如果sem的值為0,則掛起該線程。V(sem):如果有其他進程因等待sem而被掛起,則讓它恢復(fù)執(zhí)行,如果沒有線程等待sem而被掛起,則sem加上1。10.6.3信號量1、創(chuàng)建信號量在使用信號量之前,首先需要創(chuàng)建一個信號量。創(chuàng)建一個信號量的函數(shù)為semget(),其函數(shù)聲明如下:#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>int

semget(key_t,int

nsems,intflag);該函數(shù)用來打開一個新的信號量集合,或者打開一個已經(jīng)存在的信號量集合。其中,參數(shù)key表示所創(chuàng)建或打開的信號量集的鍵;參數(shù)nsems表示創(chuàng)建的信號量集中信號量的個數(shù),此參數(shù)只在創(chuàng)建一個新的信號量集時才有效;參數(shù)flag表示調(diào)用函數(shù)的操作類型,也可以用來設(shè)置信號量集的訪問權(quán)限。調(diào)用函數(shù)semget()的作用由參數(shù)key和flag決定,函數(shù)調(diào)用成功時返回值為信號量的引用標識符,調(diào)用失敗時,返回-1。2、對信號量的操作對信號量的操作使用如下函數(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是信號量集的引用id,semoparray[]是一個sembuf類型數(shù)組,sembuf結(jié)構(gòu)用于指定調(diào)用semop()函數(shù)所作的操作,數(shù)組semoparray[]中的元素的個數(shù)由nops決定。sembuf結(jié)構(gòu)如下:struct

sembuf{

usbort

sem_num;shortsem_op;shortsem_flag;}其中,sem_num指定要操作的信號量;sem_flag為操作標記;sem_op用于表示所執(zhí)行的操作,相應(yīng)取值的含義如下:sem_op>0:表示線程對資源使用完畢,交回該資源。此時信號量集的semid_ds結(jié)構(gòu)的sem_base.semval將加上sem_op的值。如果此時設(shè)置了SEM_UNDO位,則信號量的調(diào)整值將減去sem_op絕對值。sem_op=0:表示進程要等待,直到sem_base.semval的值變?yōu)?。sem_op<0:表示進程希望使用資源。此時將比較sem_base.semval和sem_op的絕對值的大小。3、對信號量的控制對信號量的控制操作是通過semctl()來山實現(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為信號量集的引用標識符,semnum用于指定信號量集中某個特定的信號量,參數(shù)cmd表示調(diào)用該函數(shù)希望執(zhí)行的操作,參數(shù)arg是semun聯(lián)合。10.7出錯處理在軟件開發(fā)時需要時刻檢查錯誤發(fā)生的各種可能,例如創(chuàng)建進程失敗、打開文件失敗等。當(dāng)錯誤發(fā)生時,程序應(yīng)當(dāng)給出提示信息并進行相應(yīng)的處理。在調(diào)用庫函數(shù)或系統(tǒng)調(diào)用函數(shù)后,絕

溫馨提示

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

評論

0/150

提交評論