線(xiàn)程管理(Thread Management) 創(chuàng )建和結束線(xiàn)程 函數: pthread_create (thread,attr,start_routine,arg) pthread_exit (status) pthread_attr_init (attr) pthread_attr_destroy (attr) 創(chuàng )建線(xiàn)程: 最初,main函數包含了一個(gè)缺省的線(xiàn)程。其它線(xiàn)程則需要程序員顯式地創(chuàng )建。 pthread_create 創(chuàng )建一個(gè)新線(xiàn)程并使之運行起來(lái)。該函數可以在程序的任何地方調用。 pthread_create參數: thread:返回一個(gè)不透明的,唯一的新線(xiàn)程標識符。 attr:不透明的線(xiàn)程屬性對象??梢灾付ㄒ粋€(gè)線(xiàn)程屬性對象,或者NULL為缺省值。 start_routine:線(xiàn)程將會(huì )執行一次的C函數。 arg: 傳遞給start_routine單個(gè)參數,傳遞時(shí)必須轉換成指向void的指針類(lèi)型。沒(méi)有參數傳遞時(shí),可設置為NULL。 一個(gè)進(jìn)程可以創(chuàng )建的線(xiàn)程最大數量取決于系統實(shí)現。 一旦創(chuàng )建,線(xiàn)程就稱(chēng)為peers,可以創(chuàng )建其它線(xiàn)程。線(xiàn)程之間沒(méi)有指定的結構和依賴(lài)關(guān)系。 Q:一個(gè)線(xiàn)程被創(chuàng )建后,怎么知道操作系統何時(shí)調度該線(xiàn)程使之運行? A:除非使用了Pthreads的調度機制,否則線(xiàn)程何時(shí)何地被執行取決于操作系統的實(shí)現。強壯的程序應該不依賴(lài)于線(xiàn)程執行的順序。 線(xiàn)程屬性: 線(xiàn)程被創(chuàng )建時(shí)會(huì )帶有默認的屬性。其中的一些屬性可以被程序員用線(xiàn)程屬性對象來(lái)修改。 pthread_attr_init 和 pthread_attr_destroy用于初始化/銷(xiāo)毀先成屬性對象。 其它的一些函數用于查詢(xún)和設置線(xiàn)程屬性對象的指定屬性。 一些屬性下面將會(huì )討論。 結束終止: 結束線(xiàn)程的方法有一下幾種: 線(xiàn)程從主線(xiàn)程(main函數的初始線(xiàn)程)返回。 線(xiàn)程調用了pthread_exit函數。 其它線(xiàn)程使用 pthread_cancel函數結束線(xiàn)程。 調用exec或者exit函數,整個(gè)進(jìn)程結束。 pthread_exit用于顯式退出線(xiàn)程。典型地,pthread_exit()函數在線(xiàn)程完成工作時(shí),不在需要時(shí)候被調用,退出線(xiàn)程。 如果main()在其他線(xiàn)程創(chuàng )建前用pthread_exit()退出了,其他線(xiàn)程將會(huì )繼續執行。否則,他們會(huì )隨著(zhù)main的結束而終止。 程序員可以可選擇的指定終止狀態(tài),當任何線(xiàn)程連接(join)該線(xiàn)程時(shí),該狀態(tài)就返回給連接(join)該線(xiàn)程的線(xiàn)程。 清理:pthread_exit()函數并不會(huì )關(guān)閉文件,任何在線(xiàn)程中打開(kāi)的文件將會(huì )一直處于打開(kāi)狀態(tài),知道線(xiàn)程結束。 討論:對于正常退出,可以免于調用pthread_exit()。當然,除非你想返回一個(gè)返回值。然而,在main中,有一個(gè)問(wèn)題,就是當main結束時(shí),其它線(xiàn)程還沒(méi)有被創(chuàng )建。如果此時(shí)沒(méi)有顯式的調用pthread_exit(),當main結束時(shí),進(jìn)程(和所有線(xiàn)程)都會(huì )終止??梢栽趍ain中調用pthread_exit(),此時(shí)盡管在main中已經(jīng)沒(méi)有可執行的代碼了,進(jìn)程和所有線(xiàn)程將保持存活狀態(tài),。 -------------------------------------------------------------------------------- 例子: Pthread 創(chuàng )建和終止 該例用pthread_create()創(chuàng )建了5個(gè)線(xiàn)程。每一個(gè)線(xiàn)程都會(huì )打印一條“Hello World”的消息,然后調用pthread_exit()終止線(xiàn)程。 Example Code - Pthread Creation and Termination #include <pthread.h> #include <stdio.h> #define NUM_THREADS 5 void *PrintHello(void *threadid) { int tid; tid = (int)threadid; printf("Hello World! It's me, thread #%d!\n", tid); pthread_exit(NULL); } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc, t; for(t=0; t<NUM_THREADS; t++){ printf("In main: creating thread %d\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t); if (rc){ printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } pthread_exit(NULL); } 線(xiàn)程管理 向線(xiàn)程傳遞參數 pthread_create()函數允許程序員想線(xiàn)程的start routine傳遞一個(gè)參數。當多個(gè)參數需要被傳遞時(shí),可以通過(guò)定義一個(gè)結構體包含所有要傳的參數,然后用pthread_create()傳遞一個(gè)指向改結構體的指針,來(lái)打破傳遞參數的個(gè)數的限制。 所有參數都應該傳引用傳遞并轉化成(void*)。 Q:怎樣安全地向一個(gè)新創(chuàng )建的線(xiàn)程傳遞數據? A:確保所傳遞的數據是線(xiàn)程安全的(不能被其他線(xiàn)程修改)。下面三個(gè)例子演示了那個(gè)應該和那個(gè)不應該。 Example 1 - Thread Argument Passing 下面的代碼片段演示了如何向一個(gè)線(xiàn)程傳遞一個(gè)簡(jiǎn)單的整數。主線(xiàn)程為每一個(gè)線(xiàn)程使用一個(gè)唯一的數據結構,確保每個(gè)線(xiàn)程傳遞的參數是完整的。 -------------------------------------------------------------------------------- int *taskids[NUM_THREADS]; for(t=0; t<NUM_THREADS; t++) { taskids[t] = (int *) malloc(sizeof(int)); *taskids[t] = t; printf("Creating thread %d\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]); ... } Example 2 - Thread Argument Passing 例子展示了用結構體向線(xiàn)程設置/傳遞參數。每個(gè)線(xiàn)程獲得一個(gè)唯一的結構體實(shí)例。 -------------------------------------------------------------------------------- struct thread_data{ int thread_id; int sum; char *message; }; struct thread_data thread_data_array[NUM_THREADS]; void *PrintHello(void *threadarg) { struct thread_data *my_data; ... my_data = (struct thread_data *) threadarg; taskid = my_data->thread_id; sum = my_data->sum; hello_msg = my_data->message; ... } int main (int argc, 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]); ... } Example 3 - Thread Argument Passing (Incorrect) 例子演示了錯誤地傳遞參數。循環(huán)會(huì )在線(xiàn)程訪(fǎng)問(wèn)傳遞的參數前改變傳遞給線(xiàn)程的地址的內容。 -------------------------------------------------------------------------------- int rc, t; for(t=0; t<NUM_THREADS; t++) { printf("Creating thread %d\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &t); ... } 線(xiàn)程管理 連接(Joining)和分離(Detaching)線(xiàn)程 函數: pthread_join (threadid,status) pthread_detach (threadid,status) pthread_attr_setdetachstate (attr,detachstate) pthread_attr_getdetachstate (attr,detachstate) 連接: “連接”是一種在線(xiàn)程間完成同步的方法。例如: pthread_join()函數阻賽調用線(xiàn)程知道threadid所指定的線(xiàn)程終止。 如果在目標線(xiàn)程中調用pthread_exit(),程序員可以在主線(xiàn)程中獲得目標線(xiàn)程的終止狀態(tài)。 連接線(xiàn)程只能用pthread_join()連接一次。若多次調用就會(huì )發(fā)生邏輯錯誤。 兩種同步方法,互斥量(mutexes)和條件變量(condition variables),稍后討論。 可連接(Joinable or Not)? 當一個(gè)線(xiàn)程被創(chuàng )建,它有一個(gè)屬性定義了它是可連接的(joinable)還是分離的(detached)。只有是可連接的線(xiàn)程才能被連接(joined),若果創(chuàng )建的線(xiàn)程是分離的,則不能連接。 POSIX標準的最終草案指定了線(xiàn)程必須創(chuàng )建成可連接的。然而,并非所有實(shí)現都遵循此約定。 使用pthread_create()的attr參數可以顯式的創(chuàng )建可連接或分離的線(xiàn)程,典型四步如下: 聲明一個(gè)pthread_attr_t數據類(lèi)型的線(xiàn)程屬性變量 用 pthread_attr_init()初始化改屬性變量 用pthread_attr_setdetachstate()設置可分離狀態(tài)屬性 完了后,用pthread_attr_destroy()釋放屬性所占用的庫資源 分離(Detaching): pthread_detach()可以顯式用于分離線(xiàn)程,盡管創(chuàng )建時(shí)是可連接的。 沒(méi)有與pthread_detach()功能相反的函數 建議: 若線(xiàn)程需要連接,考慮創(chuàng )建時(shí)顯式設置為可連接的。因為并非所有創(chuàng )建線(xiàn)程的實(shí)現都是將線(xiàn)程創(chuàng )建為可連接的。 若事先知道線(xiàn)程從不需要連接,考慮創(chuàng )建線(xiàn)程時(shí)將其設置為可分離狀態(tài)。一些系統資源可能需要釋放。 -------------------------------------------------------------------------------- 例子: Pthread Joining Example Code - Pthread Joining 這個(gè)例子演示了用Pthread join函數去等待線(xiàn)程終止。因為有些實(shí)現并不是默認創(chuàng )建線(xiàn)程是可連接狀態(tài),例子中顯式地將其創(chuàng )建為可連接的。 -------------------------------------------------------------------------------- #include <pthread.h> #include <stdio.h> #define NUM_THREADS 3 void *BusyWork(void *null) { int i; double result=0.0; for (i=0; i<1000000; i++) { result = result + (double)random(); } printf("result = %e\n",result); pthread_exit((void *) 0); } int main (int argc, char *argv[]) { pthread_t thread[NUM_THREADS]; pthread_attr_t attr; int rc, t; void *status; /* Initialize and set thread detached attribute */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for(t=0; t<NUM_THREADS; t++) { printf("Creating thread %d\n", t); rc = pthread_create(&thread[t], &attr, BusyWork, NULL); if (rc) { printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } /* Free attribute and wait for the other threads */ pthread_attr_destroy(&attr); for(t=0; t<NUM_THREADS; t++) { rc = pthread_join(thread[t], &status); if (rc) { printf("ERROR; return code from pthread_join() is %d\n", rc); exit(-1); } printf("Completed join with thread %d status= %ld\n",t, (long)status); } pthread_exit(NULL); } 線(xiàn)程管理 棧管理 函數: pthread_attr_getstacksize (attr, stacksize) pthread_attr_setstacksize (attr, stacksize) pthread_attr_getstackaddr (attr, stackaddr) pthread_attr_setstackaddr (attr, stackaddr) 防止棧問(wèn)題: POSIX標準并沒(méi)有指定線(xiàn)程棧的大小,依賴(lài)于實(shí)現并隨實(shí)現變化。 很容易超出默認的棧大小,常見(jiàn)結果:程序終止或者數據損壞。 安全和可移植的程序應該不依賴(lài)于默認的棧限制,但是取而代之的是用pthread_attr_setstacksize分配足夠的棧大小。 pthread_attr_getstackaddr和pthread_attr_setstackaddr函數可以被程序用于將棧設置在指定的內存區域。 在LC上的一些實(shí)際例子: 默認棧大小經(jīng)常變化很大,最大值也變化很大,可能會(huì )依賴(lài)于每個(gè)節點(diǎn)的線(xiàn)程數目。 Node Architecture #CPUs Memory (GB) Default Size (bytes) AMD Opteron 8 16 2,097,152 Intel IA64 4 8 33,554,432 Intel IA32 2 4 2,097,152 IBM Power5 8 32 196,608 IBM Power4 8 16 196,608 IBM Power3 16 16 98,304 -------------------------------------------------------------------------------- 例子: 棧管理 Example Code - Stack Management 這個(gè)例子演示了如何去查詢(xún)和設定線(xiàn)程棧大小。 -------------------------------------------------------------------------------- #include <pthread.h> #include <stdio.h> #define NTHREADS 4 #define N 1000 #define MEGEXTRA 1000000 pthread_attr_t attr; void *dowork(void *threadid) { double A[N][N]; int i,j,tid; size_t mystacksize; tid = (int)threadid; pthread_attr_getstacksize (&attr, &mystacksize); printf("Thread %d: stack size = %li bytes \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); } int main(int argc, char *argv[]) { pthread_t threads[NTHREADS]; size_t stacksize; int rc, t; pthread_attr_init(&attr); pthread_attr_getstacksize (&attr, &stacksize); printf("Default stack size = %li\n", stacksize); stacksize = sizeof(double)*N*N+MEGEXTRA; printf("Amount of stack needed per thread = %li\n",stacksize); pthread_attr_setstacksize (&attr, stacksize); printf("Creating threads with stack size = %li bytes\n",stacksize); for(t=0; t<NTHREADS; t++){ rc = pthread_create(&threads[t], &attr, dowork, (void *)t); if (rc){ printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } printf("Created %d threads.\n", t); pthread_exit(NULL); } 線(xiàn)程管理 其他各種函數: pthread_self () pthread_equal (thread1,thread2) pthread_self返回調用該函數的線(xiàn)程的唯一,系統分配的線(xiàn)程ID。 pthread_equal比較兩個(gè)線(xiàn)程ID,若不同返回0,否則返回非0值。 注意這兩個(gè)函數中的線(xiàn)程ID對象是不透明的,不是輕易能檢查的。因為線(xiàn)程ID是不透明的對象,所以C語(yǔ)言的==操作符不能用于比較兩個(gè)線(xiàn)程ID。 pthread_once (once_control, init_routine) pthread_once 在一個(gè)進(jìn)程中僅執行一次init_routine。任何線(xiàn)程第一次調用該函數會(huì )執行給定的init_routine,不帶參數,任何后續調用都沒(méi)有效果。 init_routine函數一般是初始化的程序 once_control參數是一個(gè)同步結構體,需要在調用pthread_once前初始化。例如: pthread_once_t once_control = PTHREAD_ONCE_INIT; 互斥量(Mutex Variables) 概述 互斥量(Mutex)是“mutual exclusion”的縮寫(xiě)?;コ饬渴菍?shí)現線(xiàn)程同步,和保護同時(shí)寫(xiě)共享數據的主要方法 互斥量對共享數據的保護就像一把鎖。在Pthreads中,任何時(shí)候僅有一個(gè)線(xiàn)程可以鎖定互斥量,因此,當多個(gè)線(xiàn)程嘗試去鎖定該互斥量時(shí)僅有一個(gè)會(huì )成功。直到鎖定互斥量的線(xiàn)程解鎖互斥量后,其他線(xiàn)程才可以去鎖定互斥量。線(xiàn)程必須輪著(zhù)訪(fǎng)問(wèn)受保護數據。 互斥量可以防止“競爭”條件。下面的例子是一個(gè)銀行事務(wù)處理時(shí)發(fā)生了競爭條件: Thread 1 Thread 2 Balance Read balance: $1000 $1000 Read balance: $1000 $1000 Deposit $200 $1000 Deposit $200 $1000 Update balance $1000+$200 $1200 Update balance $1000+$200 $1200 上面的例子,當一個(gè)線(xiàn)程使用共享數據資源時(shí),應該用一個(gè)互斥量去鎖定“Balance”。 一個(gè)擁有互斥量的線(xiàn)程經(jīng)常用于更新全局變量。確保了多個(gè)線(xiàn)程更新同樣的變量以安全的方式運行,最終的結果和一個(gè)線(xiàn)程處理的結果是相同的。這個(gè)更新的變量屬于一個(gè)“臨界區(critical section)”。 使用互斥量的典型順序如下: 創(chuàng )建和初始一個(gè)互斥量 多個(gè)線(xiàn)程嘗試去鎖定該互斥量 僅有一個(gè)線(xiàn)程可以成功鎖定改互斥量 鎖定成功的線(xiàn)程做一些處理 線(xiàn)程解鎖該互斥量 另外一個(gè)線(xiàn)程獲得互斥量,重復上述過(guò)程 最后銷(xiāo)毀互斥量 當多個(gè)線(xiàn)程競爭同一個(gè)互斥量時(shí),失敗的線(xiàn)程會(huì )阻塞在lock調用處??梢杂?#8220;trylock”替換“lock”,則失敗時(shí)不會(huì )阻塞。 當保護共享數據時(shí),程序員有責任去確認是否需要使用互斥量。如,若四個(gè)線(xiàn)程會(huì )更新同樣的數據,但僅有一個(gè)線(xiàn)程用了互斥量,則數據可能會(huì )損壞。 互斥量(Mutex Variables) 創(chuàng )建和銷(xiāo)毀互斥量 函數: pthread_mutex_init (mutex,attr) pthread_mutex_destroy (mutex) pthread_mutexattr_init (attr) pthread_mutexattr_destroy (attr) 用法: 互斥量必須用類(lèi)型pthread_mutex_t類(lèi)型聲明,在使用前必須初始化,這里有兩種方法可以初始化互斥量: 聲明時(shí)靜態(tài)地,如: pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; 動(dòng)態(tài)地用pthread_mutex_init()函數,這種方法允許設定互斥量的屬性對象attr。 互斥量初始化后是解鎖的。 attr對象用于設置互斥量對象的屬性,使用時(shí)必須聲明為pthread_mutextattr_t類(lèi)型,默認值可以是NULL。Pthreads標準定義了三種可選的互斥量屬性: 協(xié)議(Protocol): 指定了協(xié)議用于阻止互斥量的優(yōu)先級改變 優(yōu)先級上限(Prioceiling):指定互斥量的優(yōu)先級上限 進(jìn)程共享(Process-shared):指定進(jìn)程共享互斥量 注意所有實(shí)現都提供了這三個(gè)可先的互斥量屬性。 pthread_mutexattr_init()和pthread_mutexattr_destroy()函數分別用于創(chuàng )建和銷(xiāo)毀互斥量屬性對象。 pthread_mutex_destroy()應該用于釋放不需要再使用的互斥量對象。 互斥量(Mutex Variables) 鎖定和解鎖互斥量 函數: pthread_mutex_lock (mutex) pthread_mutex_trylock (mutex) pthread_mutex_unlock (mutex) 用法: 線(xiàn)程用pthread_mutex_lock()函數去鎖定指定的mutex變量,若該mutex已經(jīng)被另外一個(gè)線(xiàn)程鎖定了,該調用將會(huì )阻塞線(xiàn)程直到mutex被解鎖。 pthread_mutex_trylock() will attempt to lock a mutex. However, if the mutex is already locked, the routine will return immediately with a "busy" error code. This routine may be useful in pthread_mutex_trylock()嘗試著(zhù)去鎖定一個(gè)互斥量,然而,若互斥量已被鎖定,程序會(huì )立刻返回并返回一個(gè)忙錯誤值。該函數在優(yōu)先級改變情況下阻止死鎖是非常有用的。 線(xiàn)程可以用pthread_mutex_unlock()解鎖自己占用的互斥量。在一個(gè)線(xiàn)程完成對保護數據的使用,而其它線(xiàn)程要獲得互斥量在保護數據上工作時(shí),可以調用該函數。若有一下情形則會(huì )發(fā)生錯誤: 互斥量已經(jīng)被解鎖 互斥量被另一個(gè)線(xiàn)程占用 互斥量并沒(méi)有多么“神奇”的,實(shí)際上,它們就是參與的線(xiàn)程的“君子約定”。寫(xiě)代碼時(shí)要確信正確地鎖定,解鎖互斥量。下面演示了一種邏輯錯誤: · Thread 1 Thread 2 Thread 3 · Lock Lock · A = 2 A = A+1 A = A*B · Unlock Unlock Q:有多個(gè)線(xiàn)程等待同一個(gè)鎖定的互斥量,當互斥量被解鎖后,那個(gè)線(xiàn)程會(huì )第一個(gè)鎖定互斥量? A:除非線(xiàn)程使用了優(yōu)先級調度機制,否則,線(xiàn)程會(huì )被系統調度器去分配,那個(gè)線(xiàn)程會(huì )第一個(gè)鎖定互斥量是隨機的。 -------------------------------------------------------------------------------- 例子:使用互斥量 Example Code - Using Mutexes 例程演示了線(xiàn)程使用互斥量處理一個(gè)點(diǎn)積(dot product)計算。主數據通過(guò)一個(gè)可全局訪(fǎng)問(wèn)的數據結構被所有線(xiàn)程使用,每個(gè)線(xiàn)程處理數據的不同部分,主線(xiàn)程等待其他線(xiàn)程完成計算并輸出結果。 -------------------------------------------------------------------------------- #include <pthread.h> #include <stdio.h> #include <malloc.h> /* The following structure contains the necessary information to allow the function "dotprod" to access its input data and place its output into the structure. */ typedef struct { double *a; double *b; double sum; int veclen; } DOTDATA; /* Define globally accessible variables and a mutex */ #define NUMTHRDS 4 #define VECLEN 100 DOTDATA dotstr; pthread_t callThd[NUMTHRDS]; pthread_mutex_t mutexsum; /* The function dotprod is activated when the thread is created. All input to this routine is obtained from a structure of type DOTDATA and all output from this function is written into this structure. The benefit of this approach is apparent for the multi-threaded program: when a thread is created we pass a single argument to the activated function - typically this argument is a thread number. All the other information required by the function is accessed from the globally accessible structure. */ void *dotprod(void *arg) { /* Define and use local variables for convenience */ int i, start, end, offset, len ; double mysum, *x, *y; offset = (int)arg; len = dotstr.veclen; start = offset*len; end = start + len; x = dotstr.a; y = dotstr.b; /* Perform the dot product and assign result to the appropriate variable in the structure. */ mysum = 0; for (i=start; i<end ; i++) { mysum += (x[i] * y[i]); } /* Lock a mutex prior to updating the value in the shared structure, and unlock it upon updating. */ pthread_mutex_lock (&mutexsum); dotstr.sum += mysum; pthread_mutex_unlock (&mutexsum); pthread_exit((void*) 0); } /* The main program creates threads which do all the work and then print out result upon completion. Before creating the threads, the input data is created. Since all threads update a shared structure, we need a mutex for mutual exclusion. The main thread needs to wait for all threads to complete, it waits for each one of the threads. We specify a thread attribute value that allow the main thread to join with the threads it creates. Note also that we free up handles when they are no longer needed. */ int main (int argc, char *argv[]) { int i; double *a, *b; void *status; pthread_attr_t attr; /* Assign storage and initialize values */ 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); /* Create threads to perform the dotproduct */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for(i=0; i<NUMTHRDS; i++) { /* Each thread works on a different set of data. The offset is specified by 'i'. The size of the data for each thread is indicated by VECLEN. */ pthread_create( &callThd[i], &attr, dotprod, (void *)i); } pthread_attr_destroy(&attr); /* Wait on the other threads */ for(i=0; i<NUMTHRDS; i++) { pthread_join( callThd[i], &status); } /* After joining, print out the results and cleanup */ printf ("Sum = %f \n", dotstr.sum); free (a); free (b); pthread_mutex_destroy(&mutexsum); pthread_exit(NULL); } Serial version Pthreads version 條件變量(Condition Variables) 概述 條件變量提供了另一種同步的方式?;コ饬客ㄟ^(guò)控制對數據的訪(fǎng)問(wèn)實(shí)現了同步,而條件變量允許根據實(shí)際的數據值來(lái)實(shí)現同步。 沒(méi)有條件變量,程序員就必須使用線(xiàn)程去輪詢(xún)(可能在臨界區),查看條件是否滿(mǎn)足。這樣比較消耗資源,因為線(xiàn)程連續繁忙工作。條件變量是一種可以實(shí)現這種輪詢(xún)的方式。 條件變量往往和互斥一起使用 使用條件變量的代表性順序如下: 主線(xiàn)程(Main Thread) o 聲明和初始化需要同步的全局數據/變量(如“count”) o 生命和初始化一個(gè)條件變量對象 o 聲明和初始化一個(gè)相關(guān)的互斥量 o 創(chuàng )建工作線(xiàn)程A和B Thread A o 工作,一直到一定的條件滿(mǎn)足(如“count”等于一個(gè)指定的值) o 鎖定相關(guān)互斥量并檢查全局變量的值 o 調用pthread_cond_wait()阻塞等待Thread-B的信號。注意pthread_cond_wait()能夠自動(dòng)地并且原子地解鎖相關(guān)的互斥量,以至于它可以被Thread-B使用。 o 當收到信號,喚醒線(xiàn)程,互斥量被自動(dòng),原子地鎖定。 o 顯式解鎖互斥量 o 繼續 Thread B o 工作 o 鎖定相關(guān)互斥量 o 改變Thread-A所等待的全局變量 o 檢查全局變量的值,若達到需要的條件,像Thread-A發(fā)信號。 o 解鎖互斥量 o 繼續 Main Thread Join / Continue 條件變量(Condition Variables) 創(chuàng )建和銷(xiāo)毀條件變量 Routines: pthread_cond_init (condition,attr) pthread_cond_destroy (condition) pthread_condattr_init (attr) pthread_condattr_destroy (attr) Usage: 條件變量必須聲明為pthread_cond_t類(lèi)型,必須在使用前初始化。有兩種方式可以初始條件變量: 聲明時(shí)靜態(tài)地。如: pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER; 用pthread_cond_init()函數動(dòng)態(tài)地。創(chuàng )建的條件變量ID通過(guò)condition參數返回給調用線(xiàn)程。該方式允許設置條件變量對象的屬性,attr。 可選的attr對象用于設定條件變量的屬性。僅有一個(gè)屬性被定義:線(xiàn)程共享(process-shared),可以使條件變量被其它進(jìn)程中的線(xiàn)程看到。若要使用屬性對象,必須定義為pthread_condattr_t類(lèi)型(可以指定為NULL設為默認)。 注意所有實(shí)現都提供了線(xiàn)程共享屬性。 pthread_condattr_init()和pthread_condattr_destroy()用于創(chuàng )建和銷(xiāo)毀條件變量屬性對象。 條件變量不需要再使用時(shí),應用pthread_cond_destroy()釋放條件變量。 條件變量(Condition Variables) 在條件變量上等待(Waiting)和發(fā)送信號(Signaling) 函數: pthread_cond_wait (condition,mutex) pthread_cond_signal (condition) pthread_cond_broadcast (condition) 用法: pthread_cond_wait()阻塞調用線(xiàn)程直到指定的條件受信(signaled)。該函數應該在互斥量鎖定時(shí)調用,當在等待時(shí)會(huì )自動(dòng)解鎖互斥量。在信號被發(fā)送,線(xiàn)程被激活后,互斥量會(huì )自動(dòng)被鎖定,當線(xiàn)程結束時(shí),由程序員負責解鎖互斥量。 pthread_cond_signal()函數用于向其他等待在條件變量上的線(xiàn)程發(fā)送信號(激活其它線(xiàn)程)。應該在互斥量被鎖定后調用。 若不止一個(gè)線(xiàn)程阻塞在條件變量上,則應用pthread_cond_broadcast()向其它所以線(xiàn)程發(fā)生信號。 在調用pthread_cond_wait()前調用pthread_cond_signal()會(huì )發(fā)生邏輯錯誤。 使用這些函數時(shí)適當的鎖定和解鎖相關(guān)的互斥量是非常重要的。如: 調用pthread_cond_wait()前鎖定互斥量失敗可能導致線(xiàn)程不會(huì )阻塞。 調用pthread_cond_signal()后解鎖互斥量失敗可能會(huì )不允許相應的pthread_cond_wait()函數結束(保存阻塞)。 -------------------------------------------------------------------------------- 例子:使用條件變量 Example Code - Using Condition Variables 例子演示了使用Pthreads條件變量的幾個(gè)函數。主程序創(chuàng )建了三個(gè)線(xiàn)程,兩個(gè)線(xiàn)程工作,根系“count”變量。第三個(gè)線(xiàn)程等待count變量值達到指定的值。 -------------------------------------------------------------------------------- #include <pthread.h> #include <stdio.h> #define NUM_THREADS 3 #define TCOUNT 10 #define COUNT_LIMIT 12 int count = 0; int thread_ids[3] = {0,1,2}; pthread_mutex_t count_mutex; pthread_cond_t count_threshold_cv; void *inc_count(void *idp) { int j,i; double result=0.0; int *my_id = idp; for (i=0; i<TCOUNT; i++) { pthread_mutex_lock(&count_mutex); count++; /* Check the value of count and signal waiting thread when condition is reached. Note that this occurs while mutex is locked. */ if (count == COUNT_LIMIT) { pthread_cond_signal(&count_threshold_cv); printf("inc_count(): thread %d, count = %d Threshold reached.\n", *my_id, count); } printf("inc_count(): thread %d, count = %d, unlocking mutex\n", *my_id, count); pthread_mutex_unlock(&count_mutex); /* Do some work so threads can alternate on mutex lock */ for (j=0; j<1000; j++) result = result + (double)random(); } pthread_exit(NULL); } void *watch_count(void *idp) { int *my_id = idp; printf("Starting watch_count(): thread %d\n", *my_id); /* Lock mutex and wait for signal. Note that the pthread_cond_wait routine will automatically and atomically unlock mutex while it waits. Also, note that if COUNT_LIMIT is reached before this routine is run by the waiting thread, the loop will be skipped to prevent pthread_cond_wait from never returning. */ pthread_mutex_lock(&count_mutex); if (count<COUNT_LIMIT) { pthread_cond_wait(&count_threshold_cv, &count_mutex); printf("watch_count(): thread %d Condition signal received.\n", *my_id); } pthread_mutex_unlock(&count_mutex); pthread_exit(NULL); } int main (int argc, char *argv[]) { int i, rc; pthread_t threads[3]; pthread_attr_t attr; /* Initialize mutex and condition variable objects */ pthread_mutex_init(&count_mutex, NULL); pthread_cond_init (&count_threshold_cv, NULL); /* For portability, explicitly create threads in a joinable state */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&threads[0], &attr, inc_count, (void *)&thread_ids[0]); pthread_create(&threads[1], &attr, inc_count, (void *)&thread_ids[1]); pthread_create(&threads[2], &attr, watch_count, (void *)&thread_ids[2]); /* Wait for all threads to complete */ for (i=0; i<NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf ("Main(): Waited on %d threads. Done.\n", NUM_THREADS); /* Clean up and exit */ pthread_attr_destroy(&attr); pthread_mutex_destroy(&count_mutex); pthread_cond_destroy(&count_threshold_cv); pthread_exit(NULL); } 本文來(lái)自CSDN博客,轉載請標明出處:http://blog.csdn.net/future_fighter/archive/2009/02/05/3865071.aspx