POSIX線程程序設(shè)計(中文版)_第1頁
POSIX線程程序設(shè)計(中文版)_第2頁
POSIX線程程序設(shè)計(中文版)_第3頁
POSIX線程程序設(shè)計(中文版)_第4頁
POSIX線程程序設(shè)計(中文版)_第5頁
已閱讀5頁,還剩32頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

BlaiseBarney,LawrenceLivermoreNationalLaboratory目錄表摘要Pthreads概述什么是線程?什么是Pthreads?為什么使用Pthreads?使用線程設(shè)計程序"PthreadsAPI編譯多線程程序線程管理創(chuàng)建和終止線程向線程傳遞參數(shù)連接(Joining)和分離(Detaching)線程棧管理其它函數(shù)互斥量(MutexVariables)互斥量概述創(chuàng)建和銷毀互斥量鎖定(Locking)和解鎖(Unlocking)互斥量條件變量(ConditionVariable)條件變量概述創(chuàng)建和銷毀條件變量等待(Waiting)和發(fā)送信號(Signaling)沒有覆蓋的主題Pthread庫API參考參考資料在多處理器共享內(nèi)存的架構(gòu)中(如:對稱多處理系統(tǒng)SMP),線程可以用于實現(xiàn)程序的并行性。歷史上硬件銷售商實現(xiàn)了各種私有版本的多線程庫,使得軟件開發(fā)者不得不關(guān)心它的移植性。對于UNIX系統(tǒng),IEEEPOSIX1003.1標準定義了一個C語言多線程編程接口。依附于該標準的實現(xiàn)被稱為POSIXtheads或Pthreads。該教程介紹了Pthreads的概念、動機和設(shè)計思想。內(nèi)容包含了PthreadsAPI主要的三大類函數(shù):線程管理(ThreadManagment)、互斥量(MutexVariables)和條件變量(ConditionVariables)。向剛開始學(xué)習(xí)Pthreads的程序員提供了演示例程。適于:剛開始學(xué)習(xí)使用線程實現(xiàn)并行程序設(shè)計;對于C并行程序設(shè)計有基本了解。不熟悉并行程序設(shè)計的可以參考EC3500:IntroductionToParallelComputingoPthreads概述什么是線程?技術(shù)上,線程可以定義為:可以被操作系統(tǒng)調(diào)度的獨立的指令流。但是這是什么意思呢?對于軟件開發(fā)者,在主程序中運行的“函數(shù)過程”可以很好的描述線程的概念。?進一步,想象下主程序(a.out)包含了許多函數(shù),操作系統(tǒng)可以調(diào)度這些函數(shù),使之同時或者(和)獨立的執(zhí)行。這就描述了“多線程”程序。怎樣完成的呢??在理解線程之前,應(yīng)先對UNIX進程(process)有所了解。進程被操作系統(tǒng)創(chuàng)建,需要相當多的“額外開銷”。進程包含了程序的資源和執(zhí)行狀態(tài)信息。如下:o進程ID,進程groupID,用戶ID和groupIDo環(huán)境o工作目錄o程序指令o寄存器o棧o堆o文件描述符o信號動作(Signalactions)o共享庫o進程間通信工具(如:消息隊列,管道,信號量或共享內(nèi)存)

UserAddressSpaceUserAddressSpaceThread2dataheaproutlnelvad()g蟲()Thread1Me/tfou11ne1柿varlwa.r2marnG匸口口tm*el()UserAddressSpaceUserAddressSpaceThread2dataheaproutlnelvad()g蟲()Thread1Me/tfou11ne1柿varlwa.r2marnG匸口口tm*el()rant (}ProcessIIUserIDGroupIDStackPairPrgrm.CaRegEstersroutinQ2()va.rl\rar2varSStackPollPr^riri.CoRegistersmain()EQUt-l口亡丄cQutine2dataFilesLocksSocketsUNIXPROCESSTHREADSWITHINAUNIX

PROCESSUNIXPROCESS?線程使用并存在于進程資源中,還可以被操作系統(tǒng)調(diào)用并獨立地運行,這主要是因為線程僅僅復(fù)制必要的資源以使自己得以存在并執(zhí)行。?獨立的控制流得以實現(xiàn)是因為線程維持著自己的:o堆棧指針o寄存器o調(diào)度屬性(如:策略或優(yōu)先級)o待定的和阻塞的信號集合(Setofpendingandblockedsignals)o線程專用數(shù)據(jù)(TSD:ThreadSpecificData.)?因此,在UNIX環(huán)境下線程:o存在于進程,使用進程資源o擁有自己獨立的控制流,只要父進程存在并且操作系統(tǒng)支持o只復(fù)制必可以使得獨立調(diào)度的必要資源o可以和其他線程獨立(或非獨立的)地共享進程資源o當父進程結(jié)束時結(jié)束,或者相關(guān)類似的o是“輕型的”,因為大部分額外開銷已經(jīng)在進程創(chuàng)建時完成了?因為在同一個進程中的線程共享資源:o一個線程對系統(tǒng)資源(如關(guān)閉一個文件)的改變對所有其它線程是可以見的o兩個同樣值的指針指向相同的數(shù)據(jù)o讀寫同一個內(nèi)存位置是可能的,因此需要成員顯式地使用同步Pthreads概述什么是Pthreads??歷史上,硬件銷售商實現(xiàn)了私有版本的多線程庫。這些實現(xiàn)在本質(zhì)上各自不同,使得程序員難于開發(fā)可移植的應(yīng)用程序。?為了使用線程所提供的強大優(yōu)點,需要一個標準的程序接口。對于UNIX系統(tǒng),IEEEPOSIX1003.1c(1995)標準制訂了這一標準接口。依賴于該標準的實現(xiàn)就稱為POSIXthreads或者Pthreads?,F(xiàn)在多數(shù)硬件銷售商也提供Pthreads,附加于私有的API。?Pthreads被定義為一些C語言類型和函數(shù)調(diào)用,用pthread.h頭(包含)文件和線程庫實現(xiàn)。這個庫可以是其它庫的一部分,如libc。Pthreads概述為什么使用Pthreads?使用Pthreads的主要動機是提高潛在程序的性能。當與創(chuàng)建和管理進程的花費相比,線程可以使用操作系統(tǒng)較少的開銷,管理線程需要較少的系統(tǒng)資源。例如,下表比較了fork()函數(shù)和pthread_create()函數(shù)所用的時間。計時反應(yīng)了50,000個進程/線程的創(chuàng)建,使用時間工具實現(xiàn),單位是秒,沒有優(yōu)化標志。備注:不要期待系統(tǒng)和用戶時間加起來就是真實時間,因為這些SMP系統(tǒng)有多個CPU同時工作。這些都是近似值。平臺fork()pthread_create()realusersysrealusersysAMD2.4GHzOpteron(8cpus/node)41.0760.089.010.660.190.43IBM1.9GHzPOWER5p5-575(8cpus/node)64.2430.7827.681.750.691.10IBM1.5GHzPOWER4104.0548.6447.212.011.001.52(8cpus/node)INTEL2.4GHzXeon(2cpus/node)54.951.5420.781.640.670.90INTEL1.4GHzItanium2(4cpus/node)54.541.0722.222.031.260.67forkvsthread.txt?在同一個進程中的所有線程共享同樣的地址空間。較于進程間的通信,在許多情況下線程間的通信效率比較高,且易于使用。?較于沒有使用線程的程序,使用線程的應(yīng)用程序有潛在的性能增益和實際的優(yōu)點:oCPU使用I/O交疊工作:例如,一個程序可能有一個需要較長時間的I/O操作,當一個線程等待I/O系統(tǒng)調(diào)用完成時,CPU可以被其它線程使用。o優(yōu)先/實時調(diào)度:比較重要的任務(wù)可以被調(diào)度,替換或者中斷較低優(yōu)先級的任務(wù)。o異步事件處理:頻率和持續(xù)時間不確定的任務(wù)可以交錯。例如,web服務(wù)器可以同時為前一個請求傳輸數(shù)據(jù)和管理新請求。?考慮在SMP架構(gòu)上使用Pthreads的主要動機是獲的最優(yōu)的性能。特別的,如果一個程序使用MPI在節(jié)點通信,使用Pthreads可以使得節(jié)點數(shù)據(jù)傳輸?shù)玫斤@著提高。?例如:oMPI庫經(jīng)常用共享內(nèi)存實現(xiàn)節(jié)點任務(wù)通信,這至少需要一次內(nèi)存復(fù)制操作(進程到進程)。oPthreads沒有中間的內(nèi)存復(fù)制,因為線程和一個進程共享同樣的地址空間。沒有數(shù)據(jù)傳輸。變成cache-to-CPU或memory-to-CPU的帶寬(最壞情況),速度是相當?shù)目?。o比較如下:PlatformMPISharedMemoryBandwidth(GB/sec)PthreadsWorstCaseMemory-to-CPUBandwidth(GB/sec)AMD2.4GHzOpteron1.25.3IBM1.9GHzPOWER5p5-5754.116IBM1.5GHzP0WER42.14Intel1.4GHzXeon0.34.3Intel1.4GHzItanium21.86.4Pthreads概述使用線程設(shè)計程序丫并行編程:?在現(xiàn)代多CPU機器上,pthread非常適于并行編程??梢杂糜诓⑿谐绦蛟O(shè)計的,也可以用于pthread程序設(shè)計。?并行程序要考慮許多,如下:o用什么并行程序設(shè)計模型?o問題劃分o加載平衡(Loadbalancing)o通信o數(shù)據(jù)依賴o同步和競爭條件o內(nèi)存問題oI/O問題o程序復(fù)雜度o程序員的努力/花費/時間o…?包含這些主題超出本教程的范圍,有興趣的讀者可以快速瀏覽下“IntroductiontoParallelComputing"教程。?大體上,為了使用Pthreads的優(yōu)點,必須將任務(wù)組織程離散的,獨立的,可以并發(fā)執(zhí)行的。例如,如果routinel和routine2可以互換,相互交叉和或者)重疊,他們就可以線程化。routinelroutine?finalroutineroutinelroutine?finalroutineUnalroutineroutinelUnalroutineroutinelroutines?擁有下述特性的程序可以使用pthreads:o工作可以被多個任務(wù)同時執(zhí)行,或者數(shù)據(jù)可以同時被多個任務(wù)操作。o阻塞與潛在的長時間I/O等待。o在某些地方使用很多CPU循環(huán)而其他地方?jīng)]有。o對異步事件必須響應(yīng)。o一些工作比其他的重要(優(yōu)先級中斷)。?Pthreads也可以用于串行程序,模擬并行執(zhí)行。很好例子就是經(jīng)典的web瀏覽器,對于多數(shù)人,運行于單CPU的桌面/膝上機器,許多東西可以同時“顯示”出來。?使用線程編程的幾種常見模型:o管理者7工作者(ManogeWo/?腕f):—個單線程,作為管理器將工作分配給其它線程(工作者),典型的,管理器處理所有輸入和分配工作給其它任務(wù)。至少兩種形式的manager/worker模型比較常用:靜態(tài)worker池和動態(tài)worker池。o管道(Pipeline):任務(wù)可以被劃分為一系列子操作,每一個被串行處理,但是不同的線程并發(fā)處理。汽車裝配線可以很好的描述這個模型。oPeer:和manager/worker模型相似,但是主線程在創(chuàng)建了其它線程后,自己也參與工作。?共享內(nèi)存模型(SharedMemoryModel):?所有線程可以訪問全局,共享內(nèi)存?線程也有自己私有的數(shù)據(jù)?程序員負責對全局共享數(shù)據(jù)的同步存?。ūWo)

threadthreadprivateprivatethreadthreadprivateprivateI線程安全(Thread-safeness):?線程安全:簡短的說,指程序可以同時執(zhí)行多個線程卻不會“破壞“共享數(shù)據(jù)或者產(chǎn)生“競爭”條件的能力。?例如:假設(shè)你的程序創(chuàng)建了幾個線程,每一個調(diào)用相同的庫函數(shù):

o這個庫函數(shù)存取/修改了一個全局結(jié)構(gòu)或內(nèi)存中的位置。o當每個線程調(diào)用這個函數(shù)時,可能同時去修改這個全局結(jié)構(gòu)活內(nèi)存位置。o如果函數(shù)沒有使用同步機制去阻止數(shù)據(jù)破壞,這時,就不是線程安全的了。subA?如果你不是100%確定外部庫函數(shù)是線程安全的,自己負責所可能引發(fā)的問題。?建議:小心使用庫或者對象,當不能明確確定是否是線程安全的。若有疑慮,假設(shè)其不是線程安全的直到得以證明。可以通過不斷地使用不確定的函數(shù)找出問題所在。PthreadsAPIPthreadsAPI在ANSI/IEEEPOSIX1003.1T995標準中定義。不像MPI,該標準不是免費的,必須向IEEE購買。PthreadsAPI中的函數(shù)可以非正式的劃分為三大類:線程管理血management):第一類函數(shù)直接用于線程:創(chuàng)建(creating),分離(detaching),連接(joining)等等。包含了用于設(shè)置和查詢線程屬性(可連接,調(diào)度屬性等)的函數(shù)?;コ饬縂W“te兀eQ:第二類函數(shù)是用于線程同步的,稱為互斥量(mutexes),是"mutualexclusion"的縮寫。Mutex函數(shù)提供了創(chuàng)建,銷毀,鎖定和解鎖互斥量的功能。同時還包括了一些用于設(shè)定或修改互斥量屬性的函數(shù)。條件變量Conditionvariables):第三類函數(shù)處理共享一個互斥量的線程間的通信,基于程序員指定的條件。這類函數(shù)包括指定的條件變量的創(chuàng)建,銷毀,等待和受信(signal)。設(shè)置查詢條件變量屬性的函數(shù)也包含其中。

?命名約定:線程庫中的所有標識符都以pthread開頭RoutinePrefixFunctionalGrouppthread_線程本身和各種相關(guān)函數(shù)pthread_attr_線程屬性對象pthread_mutex_互斥量pthread_mutexattr_互斥量屬性對象pthread_cond_條件變量pthread_condattr_條件變量屬性對象pthread_key_線程數(shù)據(jù)鍵(Thread-specificdatakeys)?在API的設(shè)計中充滿了不透明對象的概念,基本調(diào)用可以創(chuàng)建或修改不透明對象。不透明的對象可以被一些屬性函數(shù)調(diào)用修改。?PthreadAPI包含了60多個函數(shù)。該教程僅限于一部分(對于剛開始學(xué)習(xí)Pthread的程序是非常有用的)。?為了可移植性,使用Pthread庫時,pthread.h頭文件必須在每個源文件中包含。?現(xiàn)行POSIX標準僅定義了C語言的使用。Fortran程序員可以嵌入C函數(shù)調(diào)用使用,有些Fortran編譯器(像IBMAIXFortran)可能提供了FortranpthreadsAPI。?關(guān)于Pthreads有些比較優(yōu)秀的書籍。其中一些在該教程的參考一節(jié)列出。編譯多線程程序?下表列出了一些編譯使用了pthreads庫程序的命令:■■Compiler/PlatformCompilerCommandDescriptionxlc_r/cc_rC(ANSI/non-ANSI)IBMxlC_rC++AIXxlf_r-qnosavexlf90_r-qnosaveFortran-usingIBM'sPthreadsAPI(non-portable)INTELicc-pthreadCLinuxicpc-pthreadC++PathScalepathcc-pthreadCLinuxpathCC-pthreadC++PGIpgcc-lpthreadCLinuxpgCC-lpthreadC++GNUgcc-pthreadGNUCLinux,AIXg++-pthreadGNUC++線程管理(ThreadManagement)創(chuàng)建和結(jié)束線程l函數(shù):Ipthread_create(thread,attr,start_routine,arg)pthread_exit(status)pthread_attr_init(attr)pthread_attr_destroy(attr)、創(chuàng)建線程:?最初,main函數(shù)包含了一個缺省的線程。其它線程則需要程序員顯式地創(chuàng)建。pthread_create創(chuàng)建一個新線程并使之運行起來。該函數(shù)可以在程序的任何地方調(diào)用。pthread_create參數(shù):thread:返回一個不透明的,唯一的新線程標識符。attr:不透明的線程屬性對象??梢灾付ㄒ粋€線程屬性對象,或者NULL為缺省值。start_routine:線程將會執(zhí)行一次的C函數(shù)。arg:傳遞給start_routii^個參數(shù),傳遞時必須轉(zhuǎn)換成指向void的指針類型。沒有參數(shù)傳遞時,可設(shè)置為NULL。一個進程可以創(chuàng)建的線程最大數(shù)量取決于系統(tǒng)實現(xiàn)。一旦創(chuàng)建,線程就稱為peers,可以創(chuàng)建其它線程。線程之間沒有指定的結(jié)構(gòu)和依賴關(guān)系。thread3?Q:—個線程被創(chuàng)建后,怎么知道操作系統(tǒng)何時調(diào)度該線程使之運行?A:除非使用了Pthreads的調(diào)度機制,否則線程何時何地被執(zhí)行取決于操作系統(tǒng)的實現(xiàn)。強壯的程序應(yīng)該不依賴于線程執(zhí)行的順序。、線程屬性:?線程被創(chuàng)建時會帶有默認的屬性。其中的一些屬性可以被程序員用線程屬性對象來修改。pthread_attr_init和pthread_attr_destroy用于初始化/銷毀先成屬性對象。?其它的一些函數(shù)用于查詢和設(shè)置線程屬性對象的指定屬性。一些屬性下面將會討論。L結(jié)束終止:?結(jié)束線程的方法有一下幾種:O線程從主線程(main函數(shù)的初始線程)返回。o線程調(diào)用了pthread_exit函數(shù)。o其它線程使用pthread_cancel函數(shù)結(jié)束線程。o調(diào)用exec或者exit函數(shù),整個進程結(jié)束。?pthread_exit用于顯式退出線程。典型地,pthread_exit()函數(shù)在線程完成工作時,不在需要時候被調(diào)用,退出線程。?如果main()在其他線程創(chuàng)建前用pthread_exit()退出了,其他線程將會繼續(xù)執(zhí)行。否則,他們會隨著main的結(jié)束而終止。?程序員可以可選擇的指定終止狀態(tài),當任何線程連接(join)該線程時,該狀態(tài)就返回給連接(join)該線程的線程。?清理:pthread_exit()函數(shù)并不會關(guān)閉文件,任何在線程中打開的文件將會一直處于打開狀態(tài),知道線程結(jié)束。?討論:對于正常退出,可以免于調(diào)用pthread_exit()。當然,除非你想返回一個返回值。然而,在main中,有一個問題,就是當main結(jié)束時,其它線程還沒有被創(chuàng)建。如果此時沒有顯式的調(diào)用pthread_exit(),當main結(jié)束時,進程(和所有線程)都會終止??梢栽趍ain中調(diào)用pthread_exit(),此時盡管在main中已經(jīng)沒有可執(zhí)行的代碼了,進程和所有線程將保持存活狀態(tài),。例子:Pthread創(chuàng)建和終止?該例用pthread_create()創(chuàng)建了5個線程。每一個線程都會打印一條“HelloWorld"的消息,然后調(diào)用pthread_exit()終止線程。3JExampleCode-PthreadCreationandTermination#include<pthread.h>#include<stdio.h>#defineNUM_THREADS5void*PrintHello(void*threadid){inttid;tid=(int)threadid;printf("HelloWorld!It'sme,thread#%d!\n",tid);pthread_exit(NULL);}intmain(intargc,char*argv[]){pthread_tthreads[NUM_THREADS];intrc,t;for(t=0;t<NUM_THREADS;t++){printf("Inmain:creatingthread%d\n",t);rc=pthread_create(&threads[t],NULL,PrintHello,(void*)t);if(rc){printf("ERROR;returncodefrompthread_create()is%d\n",rc);exit(-l);}}pthread_exit(NULL);}卜Output線程管理向線程傳遞參數(shù)?pthread_create()函數(shù)允許程序員想線程的startroutine傳遞一個參數(shù)。當多個參數(shù)需要被傳遞時,可以通過定義一個結(jié)構(gòu)體包含所有要傳的參數(shù),然后用pthread_create()傳遞一個指向改結(jié)構(gòu)體的指針,來打破傳遞參數(shù)的個數(shù)的限制。?所有參數(shù)都應(yīng)該傳引用傳遞并轉(zhuǎn)化成(void*)。?Q:怎樣安全地向一個新創(chuàng)建的線程傳遞數(shù)據(jù)?A:確保所傳遞的數(shù)據(jù)是線程安全的(不能被其他線程修改)。下面三個例子演示了那個應(yīng)該和那個不應(yīng)該。Example1-ThreadArgumentPassing下面的代碼片段演示了如何向一個線程傳遞一個簡單的整數(shù)。主線程為每一個線程使用一個唯一的數(shù)據(jù)結(jié)構(gòu),確保每個線程傳遞的參數(shù)是完整的。int*taskids[NUM_THREADS];for(t=0;t<NUM_THREADS;t++){taskids[t]二(int*)malloc(sizeof(int));*taskids[t]二t;printf("Creatingthread%d\n",t);rc=pthread_create(&threads[t],NULL,PrintHello,(void*)taskids[t]);0utput10Example2-ThreadArgumentPassing例子展示了用結(jié)構(gòu)體向線程設(shè)置/傳遞參數(shù)。每個線程獲得一個唯一的結(jié)構(gòu)體實例。struetthread_data{intthread_id;intsum;char*message;};structthread_datathread_data_array[NUM_THREADS];void*PrintHello(void*threadarg){structthread_data*my_data;my_data=(structthread_data*)threadarg;taskid=my_data—>thread_id;sum=my_data—>sum;hello_msg=my_data—>message;}intmain(intargc,char*argv[]){thread_data_array[t].thread_id=t;thread_data_array[t].sum=sum;thread_data_array[t].message=messages]t];rc=pthread_create(&threads[t],NULL,PrintHello,(void*)&thread_data_array[t]);}卜O-utputExample3-ThreadArgumentPassing (Incorrect)例子演示了錯誤地傳遞參數(shù)。循環(huán)會在線程訪問傳遞的參數(shù)前改變傳遞給線程的地址的內(nèi)容。intrc,t;for(t=0;t<NUM_THREADS;t++){printf("Creatingthread%d\n",t);rc=pthread_create(&threads[t],NULL,PrintHello,

(void*)&t);卜(void*)&t);卜Output線程管理連接(Joining)和分離(Detaching)線程I函數(shù):Ipthread_join(threadid,status)pthread_detach(threadid,status)pthread_attr_setdetachstate(attr,detachstate)pthread_attr_getdetachstate(attr,detachstate)I連接:? “連接”是一種在線程間完成同步的方法。例如:MasterThreadWorkerThreadDOWORKpthreadjoipthread,esc!MasterThreadWorkerThreadDOWORKpthreadjoipthread,esc!Worker

Thread?連接線程只能用pthread_join()連接一次。若多次調(diào)用就會發(fā)生邏輯錯誤。兩種同步方法,互斥量(mutexes)和條件變量(conditionvariables),稍后討論。?可連接(JoinableorNot)??當一個線程被創(chuàng)建,它有一個屬性定義了它是可連接的(joinable)還是分離的(detached)。只有是可連接的線程才能被連接(joined),若果創(chuàng)建的線程是分離的,則不能連接。POSIX標準的最終草案指定了線程必須創(chuàng)建成可連接的。然而,并非所有實現(xiàn)都遵循此約定。?使用pthread_create()的attr參數(shù)可以顯式的創(chuàng)建可連接或分離的線程,典型四步如下:聲明一個pthread_attr_t數(shù)據(jù)類型的線程屬性變量用pthread_attr_init()初始化改屬性變量用pthread_attr_setdetachstate()設(shè)置可分離狀態(tài)屬性完了后,用pthread_attr_destroy()釋放屬性所占用的庫資源I分離(Detaching):pthread_detach()可以顯式用于分離線程,盡管創(chuàng)建時是可連接的。?沒有與pthread_detach()功能相反的函數(shù)I建議:?若線程需要連接,考慮創(chuàng)建時顯式設(shè)置為可連接的。因為并非所有創(chuàng)建線程的實現(xiàn)都是將線程創(chuàng)建為可連接的。?若事先知道線程從不需要連接,考慮創(chuàng)建線程時將其設(shè)置為可分離狀態(tài)。一些系統(tǒng)資源可能需要釋放。例子:PthreadJoiningExampleCode-PthreadJoining這個例子演示了用Pthreadjoin函數(shù)去等待線程終止。因為有些實現(xiàn)并不是默認創(chuàng)建線程是可連接狀態(tài),例子中顯式地將其創(chuàng)建為可連接的。#defineNUM_THREADS3void*BusyWork(void*null){inti;doubleresult=0.0;for(i=0;i<1000000;i++){result二result+(double)random();}printf("result二%e\n",result);pthread_exit((void*)0);}intmain(intargc,char*argv[]){pthread_tthread[NUM_THREADS];pthread_attr_tattr;intrc,t;void*status;/*Initializeandsetthreaddetachedattribute*/pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);for(t=0;t<NUM_THREADS;t++){printf("Creatingthread%d\n",t);rc=pthread_create(&thread[t],&attr,BusyWork,NULL);if(rc){printf("ERROR;returncodefrompthread_create()is%d\n",rc);exit(-l);}}/*Freeattributeandwaitfortheotherthreads*/pthread_attr_destroy(&attr);for(t=0;t<NUM_THREADS;t++){rc=pthread_join(thread[t],&status);if(rc){printf("ERROR;returncodefrompthread_join()is%d\n",rc);exit(-l);}printf("Completedjoinwiththread%dstatus二%ld\n",t,(long)status);}pthread_exit(NULL);}—卜O-utput線程管理棧管理L函數(shù):Ipthread_attr_getstacksize(attr,stacksize)pthread_attr_setstacksize(attr,stacksize)pthread_attr_getstackaddr(attr,stackaddr)pthread_attr_setstackaddr(attr,stackaddr)I防止棧問題:POSIX標準并沒有指定線程棧的大小,依賴于實現(xiàn)并隨實現(xiàn)變化。?很容易超出默認的棧大小,常見結(jié)果:程序終止或者數(shù)據(jù)損壞。?安全和可移植的程序應(yīng)該不依賴于默認的棧限制,但是取而代之的是用pthread_attr_setstacksize分配足夠的棧大小。pthread_attr_getstackaddr和pthread_attr_setstackaddr函數(shù)可以被程序用于將棧設(shè)置在指定的內(nèi)存區(qū)域。卜在lc上的一些實際例子:?默認棧大小經(jīng)常變化很大,最大值也變化很大,可能會依賴于每個節(jié)點的線程數(shù)目。NodeArchitecture#CPUsMemory(GB)DefaultSize(bytes)AMDOpteron8162,097,152IntelIA644833,554,432IntelIA32242,097,152IBMPower5832196,608IBMPower4816196,608IBMPower316169&304例子:棧管理ExampleCode-StackManagement這個例子演示了如何去查詢和設(shè)定線程棧大小。#include<pthread.h>#include<stdio.h>#defineNTHREADS4#defineN1000#defineMEGEXTRA1000000pthread_attr_tattr;void*dowork(void*threadid){doubleA[N][N];inti,j,tid;size_tmystacksize;tid=(int)threadid;pthread_attr_getstacksize(&attr,&mystacksize);printf("Thread%d:stacksize=%libytes\n",tid,mystacksize);for(i=0;i<N;i++)for(j=0;j<N;j++)A[i][j]=((i*j)/3.452)+(N-i);pthread_exit(NULL);}intmain(intargc,charpthread_self返回調(diào)用該函數(shù)的線程的唯一,系統(tǒng)分配的線程ID。pthread_self返回調(diào)用該函數(shù)的線程的唯一,系統(tǒng)分配的線程ID。 pthread_equal比較兩個線程ID,若不同返回0,否則返回非0值。?注意這兩個函數(shù)中的線程ID對象是不透明的,不是輕易能檢查的。因為線程ID是不透明的對象,所以C語言的==操作符不能用于比較兩個線程ID。{pthread_tthreads[NTHREADS];size_tstacksize;intrc,t;pthread_attr_init(&attr);pthread_attr_getstacksize(&attr,&stacksize);printf("Defaultstacksize=%li\n",stacksize);stacksize=sizeof(double)*N*N+MEGEXTRA;printf("Amountofstackneededperthread=%li\n",stacksize);pthread_attr_setstacksize(&attr,stacksize);printf("Creatingthreadswithstacksize=%libytes\n",stacksize);for(t=0;t〈NTHREADS;t++){rc=pthread_create(&threads[t],&attr,dowork,(void*)t);if(rc){printf("ERROR;returncodefrompthread_create()is%d\n",rc);exit(-l);}}printf("Created%dthreads.\n",t);pthread_exit(NULL);}其他各種函數(shù):pthread_self()pthread_equal(thread1,thread2)pthread_once(once_control,init_routine)pthread_once在一個進程中僅執(zhí)行一次init_routine。任何線程第一次調(diào)用該函數(shù)會執(zhí)行給定的init_routine,不帶參數(shù),任何后續(xù)調(diào)用都沒有效果。init_routine函數(shù)一般是初始化的程序once_control參數(shù)是一個同步結(jié)構(gòu)體,需要在調(diào)用pthread_once前初始化。例如:pthread_once_tonce_control=PTHREAD_ONCE_INIT;互斥量(MutexVariables)概述?互斥量(Mutex)是“皿utualexclusion"的縮寫?;コ饬渴菍崿F(xiàn)線程同步,和保護同時寫共享數(shù)據(jù)的主要方法互斥量對共享數(shù)據(jù)的保護就像一把鎖。在Pthreads中,任何時候僅有一個線程可以鎖定互斥量,因此,當多個線程嘗試去鎖定該互斥量時僅有一個會成功。直到鎖定互斥量的線程解鎖互斥量后,其他線程才可以去鎖定互斥量。線程必須輪著訪問受保護數(shù)據(jù)。互斥量可以防止“競爭"條件。下面的例子是一個銀行事務(wù)處理時發(fā)生了競爭條件:Thread1Thread2BalanceReadbalance:$1000$1000Readbalance:$1000$1000Deposit$200$1000Deposit$200$1000Updatebalance$1000+$200$1200Updatebalance$1000+$200$1200?上面的例子,當一個線程使用共享數(shù)據(jù)資源時,應(yīng)該用一個互斥量去鎖定“Balance"。?一個擁有互斥量的線程經(jīng)常用于更新全局變量。確保了多個線程更新同樣的變量以安全的方式運行,最終的結(jié)果和一個線程處理的結(jié)果是相同的。這個更新的變量屬于一個“臨界區(qū)(criticalsection)"。?使用互斥量的典型順序如下:o創(chuàng)建和初始一個互斥量o多個線程嘗試去鎖定該互斥量o僅有一個線程可以成功鎖定改互斥量o鎖定成功的線程做一些處理o線程解鎖該互斥量o另外一個線程獲得互斥量,重復(fù)上述過程o最后銷毀互斥量?當多個線程競爭同一個互斥量時,失敗的線程會阻塞在lock調(diào)用處??梢杂谩皌rylock”替換“l(fā)ock",則失敗時不會阻塞。?當保護共享數(shù)據(jù)時,程序員有責任去確認是否需要使用互斥量。如,若四個線程會更新同樣的數(shù)據(jù),但僅有一個線程用了互斥量,則數(shù)據(jù)可能會損壞?;コ饬?MutexVariables)創(chuàng)建和銷毀互斥量l函數(shù):Ipthread_mutex_init(mutex,attr)pthread_mutex_destroy(mutex)pthread_mutexattr_init(attr)pthread_mutexattr_destroy(attr)?互斥量必須用類型pthread_mutex_t類型聲明,在使用前必須初始化,這里有兩種方法可以初始化互斥量:聲明時靜態(tài)地,如:pthread_mutex_tmymutex=PTHREAD_MUTEX_INITIALIZER;動態(tài)地用pthread_mutex_init()函數(shù),這種方法允許設(shè)定互斥量的屬性對象attr?;コ饬砍跏蓟笫墙怄i的。attr對象用于設(shè)置互斥量對象的屬性,使用時必須聲明為pthread_mutextattr_t類型,默認值可以是NULL。Pthreads標準定義了三種可選的互斥量屬性:o協(xié)議(Protocol):指定了協(xié)議用于阻止互斥量的優(yōu)先級改變o優(yōu)先級上限(Prioceiling):指定互斥量的優(yōu)先級上限o進程共享(Process-shared):指定進程共享互斥量注意所有實現(xiàn)都提供了這三個可先的互斥量屬性。pthread_mutexattr_init()和pthread_mutexattr_destroy()函數(shù)分另U用于創(chuàng)建和銷毀互斥量屬性對象。pthread_mutex_destroy()應(yīng)該用于釋放不需要再使用的互斥量對象。互斥量(MutexVariables)鎖定和解鎖互斥量I函數(shù):Ipthread_mutex_lock(mutex)pthread__mutex_trylock(mutex)pthread_mutex_unlock(mutex)I用法:線程用pthread_mutex_lock()函數(shù)去鎖定指定的mutex變量,若該mutex已經(jīng)被另外一個線程鎖定了,該調(diào)用將會阻塞線程直到mutex被解鎖。pthread_mutex_trylock()willattempttolockamutex.However,fthemutexisalreadylocked,theroutinewillreturnimmediatelywitha"busy"errorcode.Thisroutinemaybeusefulinpthread_mutex_trylock()嘗試著去鎖定一個互斥量,然而,若互斥量已被鎖定,程序會立刻返回并返回一個忙錯誤值。該函數(shù)在優(yōu)先級改變情況下阻止死鎖是非常有用的。?線程可以用pthread_mutex_unlock()解鎖自己占用的互斥量。在一個線程完成對保護數(shù)據(jù)的使用,而其它線程要獲得互斥量在保護數(shù)據(jù)上工作時,可以調(diào)用該函數(shù)。若有一下情形則會發(fā)生錯誤:o互斥量已經(jīng)被解鎖o互斥量被另一個線程占用?互斥量并沒有多么“神奇”的,實際上,它們就是參與的線程的“君子約定”。寫代碼時要確信正確地鎖定,解鎖互斥量。下面演示了一種邏輯錯誤:Thread1Thread2Thread3LockLockA=2A=A+1A=A*BUnlockUnlock?Q:有多個線程等待同一個鎖定的互斥量,當互斥量被解鎖后,那個線程會第一個鎖定互斥量?A:除非線程使用了優(yōu)先級調(diào)度機制,否則,線程會被系統(tǒng)調(diào)度器去分配,那個線程會第一個鎖定互斥量是隨機的。例子:使用互斥量ExampleCode-UsingMutexes例程演示了線程使用互斥量處理一個點積(dotproduct)計算。主數(shù)據(jù)通過一個可全局訪問的數(shù)據(jù)結(jié)構(gòu)被所有線程使用,每個線程處理數(shù)據(jù)的不同部分,主線程等待其他線程完成計算并輸出結(jié)果。#include<pthread.h>#include<stdio.h>#include<malloc.h>/*Thefollowingstructurecontainsthenecessaryinformationtoallowthefunction"dotprod"toaccessitsinputdataandplaceitsoutputintothestructure.*/typedefstruct{TOC\o"1-5"\h\zdouble *a;double *b;double sum;intveclen;}DOTDATA;/*Definegloballyaccessiblevariablesandamutex*/#defineNUMTHRDS4#defineVECLEN100DOTDATAdotstr;pthread_tcallThd[NUMTHRDS];pthread_mutex_tmutexsum;/*Thefunctiondotprodisactivatedwhenthethreadiscreated.AllinputtothisroutineisobtainedfromastructureoftypeDOTDATAandalloutputfromthisfunctioniswrittenintothisstructure.Thebenefitofthisapproachisapparentforthmulti—threadedprogram:whenathreadiscreatedwepassasingleargumenttotheactivatedfunction—typicallythisargumentisathreadnumber.Alltheotherinformationrequiredbythefunctionisaccessedfromthegloballyaccessiblestructure.*/void*dotprod(void*arg){/*Defineanduselocalvariablesforconvenience*/inti,start,end,offset,len;doublemysum,*x,*y;offset二(int)arg;len=dotstr.veclen;start二offset*len;end=start+len;x=dotstr.a;y=dotstr.b;/*Performthedotproductandassignresulttotheappropriatevariableinthestructure.*/mysum=0;for(i=start;i<end;i++){mysum+=(x[i]*y[i]);}/*Lockamutexpriortoupdatingthevalueinthesharedstrueture,andunlockituponupdating.*/pthread_mutex_loek(&mutexsum);dotstr.sum+=mysum;pthread_mutex_unlock(&mutexsum);pthread_exit((void*)0);}/*Themainprogramcreatesthreadswhichdoalltheworkandthenprintoutresultuponcompletion.Beforecreatingthethreads,theinputdataiscreated.Sinceallthreadsupdateasharedstructure,weneedamutexformutualexclusion.Themainthreadneedstowaitforallthreadstocomplete,itwaitsforeachoneofthethreads.Wespecifyathreadattributevaluethatallowthemainthreadtojoinwitthethreadsitcreates.Notealsothatwefreeuphandleswhentheyarenolongerneeded.*/intmain(intargc,char*argv[]){inti;double*a,*b;void*status;pthread_attr_tattr;/*Assignstorageandinitializevalues*/a=(double*)malloc(NUMTHRDS*VECLEN*sizeof(double));b=(double*)malloc(NUMTHRDS*VECLEN*sizeof(double));for(i=0;i<VECLEN*NUMTHRDS;i++){a[i]=1.0;b[i]=a[i];}dotstr.veclen=VECLEN;dotstr.a=a;

dotstr.b=b;dotstr.sum=0;pthread_mutex_init(&mutexsum,NULL);/*Createthreadstoperformthedotproduct*/pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);for(i=0;i<NUMTHRDS;i++){/*Eachthreadworksonadifferentsetofdata.Theoffsetisspecifiedby'i'.ThesizeofthedataforeachthreadisindicatedbyVECLEN.*/pthread_create(&callThd[i],&attr,dotprod,(void*)i)}pthread_attr_destroy(&attr);/*Waitontheotherthreads*/for(i=0;i<NUMTHRDS;i++){pthread_join(callThd[i],&status);}/*Afterjoining,printouttheresultsandcleanup*/printf("Sum=%f\n",dotstr.sum);free(a);free(b);pthread_mutex_destroy(&mutexsum);pthread_exit(NULL);}、Source、SourceSerialversionPthreadsversion條件變量(ConditionVariables)

概述?條件變量提供了另一種同步的方式?;コ饬客ㄟ^控制對數(shù)據(jù)的訪問實現(xiàn)了同步,而條件變量允許根據(jù)實際的數(shù)據(jù)值來實現(xiàn)同步。?沒有條件變量,程序員就必須使用線程去輪詢(可能在臨界區(qū)),查看條件是否滿足。這樣比較消耗資源,因為線程連續(xù)繁忙工作。條件變量是一種可以實現(xiàn)這種輪詢的方式。?條件變量往往和互斥一起使用?使用條件變量的代表性順序如下:主線程(MainThread)oooo聲明和初始化需要同步的全局數(shù)據(jù)/變量(如“oooo創(chuàng)建工作線程A和BThreadAThreadBo工作,一直到一定的o工作條件滿足(如“count”等于o鎖定相關(guān)互斥量一個指定的值)o改變Thread-A所等

溫馨提示

  • 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)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論