1. 程式人生 > >linux多執行緒程式設計 同步與互斥

linux多執行緒程式設計 同步與互斥

——本文一個例子展開,介紹Linux下面執行緒的操作、多執行緒的同步和互斥。

前言

執行緒?為什麼有了程序還需要執行緒呢,他們有什麼區別?使用執行緒有什麼優勢呢?還有多執行緒程式設計的一些細節問題,如執行緒之間怎樣同步、互斥,這些東西將在本文中介紹。我在某QQ群裡見到這樣一道面試題:

是否熟悉POSIX多執行緒程式設計技術?如熟悉,編寫程式完成如下功能:

1)有一int型全域性變數g_Flag初始值為0;

2) 在主線稱中起動執行緒1,列印“this is thread1”,並將g_Flag設定為1

3) 在主線稱中啟動執行緒2,列印“this is thread2”,並將g_Flag設定為2

4) 執行緒序1需要線上程2退出後才能退出

5) 主執行緒在檢測到g_Flag從1變為2,或者從2變為1的時候退出

我們帶著這題開始這篇文章,結束之後,大家就都會做了。本文的框架如下:

  • 1、程序與執行緒
  • 2、使用執行緒的理由
  • 3、有關執行緒操作的函式
  • 4、執行緒之間的互斥
  • 5、執行緒之間的同步
  • 6、試題最終程式碼

1、程序與執行緒

程序是程式執行時的一個例項,即它是程式已經執行到何種程度的資料結構的彙集。從核心的觀點看,程序的目的就是擔當分配系統資源(CPU時間、記憶體等)的基本單位

執行緒是程序的一個執行流,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位。一個程序由幾個執行緒組成(擁有很多相對獨立的執行流的使用者程式共享應用程式的大部分資料結構),執行緒與同屬一個程序的其他的執行緒共享程序所擁有的全部資源。

"程序——資源分配的最小單位,執行緒——程式執行的最小單位"

程序有獨立的地址空間,一個程序崩潰後,在保護模式下不會對其它程序產生影響,而執行緒只是一個程序中的不同執行路徑。執行緒有自己的堆疊和區域性變數,但執行緒沒有單獨的地址空間,一個執行緒死掉就等於整個程序死掉,所以多程序的程式要比多執行緒的程式健壯,但在程序切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變數的併發操作,只能用執行緒,不能用程序。

2、使用執行緒的理由

從上面我們知道了程序與執行緒的區別,其實這些區別也就是我們使用執行緒的理由。總的來說就是:程序有獨立的地址空間,執行緒沒有單獨的地址空間(同一程序內的執行緒共享程序的地址空間)

。(下面的內容摘自Linux下的多執行緒程式設計

使用多執行緒的理由之一是和程序相比,它是一種非常"節儉"的多工操作方式。我們知道,在Linux系統下,啟動一個新的程序必須分配給它獨立的地址空間,建立眾多的資料表來維護它的程式碼段、堆疊段和資料段,這是一種"昂貴"的多工工作方式。而運行於一個程序中的多個執行緒,它們彼此之間使用相同的地址空間,共享大部分資料,啟動一個執行緒所花費的空間遠遠小於啟動一個程序所花費的空間,而且,執行緒間彼此切換所需的時間也遠遠小於程序間切換所需要的時間。據統計,總的說來,一個程序的開銷大約是一個執行緒開銷的30倍左右,當然,在具體的系統上,這個資料可能會有較大的區別。

使用多執行緒的理由之二是執行緒間方便的通訊機制。對不同程序來說,它們具有獨立的資料空間,要進行資料的傳遞只能通過通訊的方式進行,這種方式不僅費時,而且很不方便。執行緒則不然,由於同一程序下的執行緒之間共享資料空間,所以一個執行緒的資料可以直接為其它執行緒所用,這不僅快捷,而且方便。當然,資料的共享也帶來其他一些問題,有的變數不能同時被兩個執行緒所修改,有的子程式中宣告為static的資料更有可能給多執行緒程式帶來災難性的打擊,這些正是編寫多執行緒程式時最需要注意的地方。

除了以上所說的優點外,不和程序比較,多執行緒程式作為一種多工、併發的工作方式,當然有以下的優點:

  • 提高應用程式響應。這對圖形介面的程式尤其有意義,當一個操作耗時很長時,整個系統都會等待這個操作,此時程式不會響應鍵盤、滑鼠、選單的操作,而使用多執行緒技術,將耗時長的操作(time consuming)置於一個新的執行緒,可以避免這種尷尬的情況。
  • 使多CPU系統更加有效。作業系統會保證當執行緒數不大於CPU數目時,不同的執行緒運行於不同的CPU上。
  • 改善程式結構。一個既長又複雜的程序可以考慮分為多個執行緒,成為幾個獨立或半獨立的執行部分,這樣的程式會利於理解和修改。

從函式呼叫上來說,程序建立使用fork()操作;執行緒建立使用clone()操作。

3、有關執行緒操作的函式

[cpp] view plaincopyprint?
  1. #include <pthread.h>
  2. int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);  
  3. int pthread_join (pthread_t tid, void ** status);  
  4. pthread_t pthread_self (void);  
  5. int pthread_detach (pthread_t tid);  
  6. void pthread_exit (void *status);  
  7. <SPAN style="FONT-FAMILY: Arial; BACKGROUND-COLOR: #ffffff"></SPAN>  
#include <pthread.h>
 
int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);
int pthread_join (pthread_t tid, void ** status);
pthread_t pthread_self (void);
int pthread_detach (pthread_t tid);
void pthread_exit (void *status);
pthread_create用於建立一個執行緒,成功返回 0,否則返回 -1。
  • pthread_t *tid:執行緒id的型別為pthread_t,通常為無符號整型,當呼叫pthread_create成功時,通過*tid指標返回。
  • const pthread_attr_t *attr:指定建立執行緒的屬性,如執行緒優先順序、初始棧大小、是否為守護程序等。可以使用NULL來使用預設值,通常情況下我們都是使用預設值。
  • void *(*func) (void *):函式指標func,指定當新的執行緒建立之後,將執行的函式。
  • void *arg:執行緒將執行的函式的引數。如果想傳遞多個引數,請將它們封裝在一個結構體中。

pthread_join用於等待某個執行緒退出,成功返回 0,否則返回 -1。

  • pthread_t tid:指定要等待的執行緒ID
  • void ** status:如果不為NULL,那麼執行緒的返回值儲存在status指向的空間中(這就是為什麼status是二級指標的原因!這種才引數也稱為“值-結果”引數)。

pthread_self用於返回當前執行緒的ID。

pthread_detach用於是指定執行緒變為分離狀態,就像程序脫離終端而變為後臺程序類似。成功返回 0,否則返回 -1。變為分離狀態的執行緒,如果執行緒退出,它的所有資源將全部釋放。而如果不是分離狀態,執行緒必須保留它的執行緒ID,退出狀態直到其它執行緒對它呼叫了pthread_join

程序也是類似,這也是當我們開啟程序管理器的時候,發現有很多僵死程序的原因!也是為什麼一定要有僵死這個程序狀態。

pthread_exit用於終止執行緒,可以指定返回值,以便其他執行緒通過pthread_join函式獲取該執行緒的返回值。

  • void *status:指標執行緒終止的返回值。

知道了這些函式之後,我們試圖來完成本文一開始的問題:

1)有一int型全域性變數g_Flag初始值為0;

2)在主線稱中起動執行緒1,列印“this is thread1”,並將g_Flag設定為1

3)在主線稱中啟動執行緒2,列印“this is thread2”,並將g_Flag設定為2

這3點很簡單嘛!!!不就是呼叫pthread_create建立執行緒。程式碼如下:

這樣就完成了1)、2)、3)這三點要求。編譯執行得如下結果: [cpp] view plaincopyprint?
  1. /* 
  2.  * 1)有一int型全域性變數g_Flag初始值為0; 
  3.  * 
  4.  * 2)在主線稱中起動執行緒1,列印“this is thread1”,並將g_Flag設定為1 
  5.  * 
  6.  * 3)在主線稱中啟動執行緒2,列印“this is thread2”,並將g_Flag設定為2 
  7.  * 
  8.  */
  9. #include<stdio.h>
  10. #include<stdlib.h>
  11. #include<pthread.h>
  12. #include<errno.h>
  13. #include<unistd.h>
  14. int g_Flag=0;  
  15. void* thread1(void*);  
  16. void* thread2(void*);  
  17. /* 
  18.  * when program is started, a single thread is created, called the initial thread or main thread. 
  19.  * Additional threads are created by pthread_create. 
  20.  * So we just need to create two thread in main(). 
  21.  */
  22. int main(int argc, char** argv)  
  23. {  
  24.     printf("enter main\n");  
  25.     pthread_t tid1, tid2;  
  26. int rc1=0, rc2=0;  
  27.     rc2 = pthread_create(&tid2, NULL, thread2, NULL);  
  28. if(rc2 != 0)  
  29.         printf("%s: %d\n",__func__, strerror(rc2));  
  30.     rc1 = pthread_create(&tid1, NULL, thread1, &tid2);  
  31. if(rc1 != 0)  
  32.         printf("%s: %d\n",__func__, strerror(rc1));  
  33.     printf("leave main\n");  
  34.     exit(0);      
  35. }  
  36. /* 
  37.  * thread1() will be execute by thread1, after pthread_create() 
  38.  * it will set g_Flag = 1; 
  39.  */
  40. void* thread1(void* arg)  
  41. {  
  42.     printf("enter thread1\n");  
  43.     printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  44.     g_Flag = 1;  
  45.     printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  46.     printf("leave thread1\n");  
  47.     pthread_exit(0);  
  48. }  
  49. /* 
  50.  * thread2() will be execute by thread2, after pthread_create() 
  51.  * it will set g_Flag = 2; 
  52.  */
  53. void* thread2(void* arg)  
  54. {  
  55.     printf("enter thread2\n");  
  56.     printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  57.     g_Flag = 2;  
  58.     printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  59.     printf("leave thread2\n");  
  60.     pthread_exit(0);  
  61. }  
/*
 * 1)有一int型全域性變數g_Flag初始值為0;
 *
 * 2)在主線稱中起動執行緒1,列印“this is thread1”,並將g_Flag設定為1
 *
 * 3)在主線稱中啟動執行緒2,列印“this is thread2”,並將g_Flag設定為2
 *
 */
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>

int g_Flag=0;

void* thread1(void*);
void* thread2(void*);

/*
 * when program is started, a single thread is created, called the initial thread or main thread.
 * Additional threads are created by pthread_create.
 * So we just need to create two thread in main().
 */
int main(int argc, char** argv)
{
	printf("enter main\n");
	pthread_t tid1, tid2;
	int rc1=0, rc2=0;
	rc2 = pthread_create(&tid2, NULL, thread2, NULL);
	if(rc2 != 0)
		printf("%s: %d\n",__func__, strerror(rc2));

	rc1 = pthread_create(&tid1, NULL, thread1, &tid2);
	if(rc1 != 0)
		printf("%s: %d\n",__func__, strerror(rc1));
	printf("leave main\n");
	exit(0);	
}
/*
 * thread1() will be execute by thread1, after pthread_create()
 * it will set g_Flag = 1;
 */
void* thread1(void* arg)
{
	printf("enter thread1\n");
	printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	g_Flag = 1;
	printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	printf("leave thread1\n");
	pthread_exit(0);
}

/*
 * thread2() will be execute by thread2, after pthread_create()
 * it will set g_Flag = 2;
 */
void* thread2(void* arg)
{
	printf("enter thread2\n");
	printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	g_Flag = 2;
	printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	printf("leave thread2\n");
	pthread_exit(0);
}

[email protected]:~/workspace/pthead_test$ gcc -lpthread test.c

如果程式中使用到了pthread庫中的函式,除了要#include<pthread.h>,在編譯的時候還有加上-lpthread 選項。
[email protected]:~/workspace/pthead_test$ ./a.out
enter main
enter thread2
this is thread2, g_Flag: 0, thread id is 3079588720
this is thread1, g_Flag: 2, thread id is 3079588720
leave thread2
leave main
enter thread1
this is thread1, g_Flag: 2, thread id is 3071196016
this is thread1, g_Flag: 1, thread id is 3071196016
leave thread1
但是執行結果不一定是上面的,還有可能是:

[email protected]:~/workspace/pthead_test$ ./a.out
enter main
leave main
enter thread1
this is thread1, g_Flag: 0, thread id is 3069176688
this is thread1, g_Flag: 1, thread id is 3069176688
leave thread1

或者是:

[email protected]:~/workspace/pthead_test$ ./a.out
enter main
leave main
等等。這也很好理解因為,這取決於主執行緒main函式何時終止,執行緒thread1、thread2是否能夠來得急執行它們的函式。這也是多執行緒程式設計時要注意的問題,因為有可能一個執行緒會影響到整個程序中的所有其它執行緒!如果我們在main函式退出前,sleep()一段時間,就可以保證thread1、thread2來得及執行。

吸血蝙蝠Attention:大家肯定已經注意到了,我們線上程函式thread1()、thread2()執行完之前都呼叫了pthread_exit。如果我是呼叫exit()又或者是return會怎樣呢?自己動手試試吧!

pthread_exit()用於執行緒退出,可以指定返回值,以便其他執行緒通過pthread_join()函式獲取該執行緒的返回值。
return是函式返回,只有執行緒函式return,執行緒才會退出。
exit是程序退出,如果線上程函式中呼叫exit,程序中的所有函式都會退出!

“4) 執行緒序1需要線上程2退出後才能退出”第4點也很容易解決,直接在thread1的函式退出之前呼叫pthread_join就OK了。

4、執行緒之間的互斥

上面的程式碼似乎很好的解決了問題的前面4點要求,其實不然!!!因為g_Flag是一個全域性變數,執行緒thread1和thread2可以同時對它進行操作,需要對它進行加鎖保護,thread1和thread2要互斥訪問才行。下面我們就介紹如何加鎖保護——互斥鎖。

互斥鎖:

         使用互斥鎖(互斥)可以使執行緒按順序執行。通常,互斥鎖通過確保一次只有一個執行緒執行程式碼的臨界段來同步多個執行緒。互斥鎖還可以保護單執行緒程式碼。

    mutex 是一種簡單的加鎖的方法來控制對共享資源的存取。這個互斥鎖只有兩種狀態,也就是上鎖和解鎖,可以把互斥鎖看作某種意義上的全域性變數。在同一時刻只能有一個執行緒掌握某個互斥上的鎖,擁有上鎖狀態的執行緒能夠對共享資源進行操作。若其他執行緒希望上鎖一個已經上鎖了的互斥鎖,則該執行緒就會掛起,直到上鎖的執行緒釋放掉互斥鎖為止。可以說,這把互斥鎖使得共享資源按序在各個執行緒中操作。 
互斥鎖的操作主要包括以下幾個步驟。 
·  互斥鎖初始化:pthread_mutex_init 
·  互斥鎖上鎖:pthread_mutex_lock 
·  互斥鎖判斷上鎖:pthread_mutex_trylock 
·  互斥鎖接鎖:pthread_mutex_unlock 
·  消除互斥鎖:pthread_mutex_destroy 
其中,互斥鎖可以分為快速互斥鎖、遞迴互斥鎖和檢錯互斥鎖。這三種鎖的區別主要在於其他未佔有互斥鎖的執行緒在希望得到互斥鎖時的是否需要阻塞等待。快速鎖是指呼叫執行緒會阻塞直至擁有互斥鎖的執行緒解鎖為止。遞迴互斥鎖能夠成功地返回並且增加呼叫執行緒在互斥上加鎖的次數,而檢錯互斥鎖則為快速互斥鎖的非阻塞版本,它會立即返回並返回一個錯誤資訊。 

互斥鎖的相關操作函式如下:

[cpp] view plaincopyprint?
  1. #include <pthread.h> 
  2. int pthread_mutex_lock(pthread_mutex_t * mptr);   
  3. int pthread_mutex_unlock(pthread_mutex_t * mptr);   
  4. //Both return: 0 if OK, positive Exxx value on error
#include <pthread.h> 

int pthread_mutex_lock(pthread_mutex_t * mptr); 
int pthread_mutex_unlock(pthread_mutex_t * mptr); 
//Both return: 0 if OK, positive Exxx value on error

在對臨界資源進行操作之前需要pthread_mutex_lock先加鎖,操作完之後pthread_mutex_unlock再解鎖。而且在這之前需要宣告一個pthread_mutex_t型別的變數,用作前面兩個函式的引數。具體程式碼見第5節。

5、執行緒之間的同步

第5點——主執行緒在檢測到g_Flag從1變為2,或者從2變為1的時候退出。就需要用到執行緒同步技術!執行緒同步需要條件變數。

條件變數:

使用條件變數可以以原子方式阻塞執行緒,直到某個特定條件為真為止。條件變數始終與互斥鎖一起使用。對條件的測試是在互斥鎖(互斥)的保護下進行的。

如果條件為假,執行緒通常會基於條件變數阻塞,並以原子方式釋放等待條件變化的互斥鎖。如果另一個執行緒更改了條件,該執行緒可能會向相關的條件變數發出訊號,從而使一個或多個等待的執行緒執行以下操作:

  • 喚醒
  • 再次獲取互斥鎖
  • 重新評估條件

在以下情況下,條件變數可用於在程序之間同步執行緒:

  • 執行緒是在可以寫入的記憶體中分配的
  • 記憶體由協作程序共享

使用條件變數可以以原子方式阻塞執行緒,直到某個特定條件為真為止。”即可用到第5點,主執行緒main函式阻塞於等待g_Flag從1變為2,或者從2變為1。條件變數的相關函式如下:

[cpp] view plaincopyprint?
  1. #include <pthread.h>
  2. int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);   
  3. int pthread_cond_signal(pthread_cond_t *cptr);   
  4. //Both return: 0 if OK, positive Exxx value on error
#include <pthread.h>
 
int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr); 
int pthread_cond_signal(pthread_cond_t *cptr); 
//Both return: 0 if OK, positive Exxx value on error

pthread_cond_wait用於等待某個特定的條件為真,pthread_cond_signal用於通知阻塞的執行緒某個特定的條件為真了。在呼叫者兩個函式之前需要宣告一個pthread_cond_t型別的變數,用於這兩個函式的引數。

為什麼條件變數始終與互斥鎖一起使用,對條件的測試是在互斥鎖(互斥)的保護下進行的呢?因為“某個特性條件”通常是在多個執行緒之間共享的某個變數。互斥鎖允許這個變數可以在不同的執行緒中設定和檢測。

通常,pthread_cond_wait只是喚醒等待某個條件變數的一個執行緒。如果需要喚醒所有等待某個條件變數的執行緒,需要呼叫:

int pthread_cond_broadcast (pthread_cond_t * cptr);

預設情況下面,阻塞的執行緒會一直等待,知道某個條件變數為真。如果想設定最大的阻塞時間可以呼叫:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);

如果時間到了,條件變數還沒有為真,仍然返回,返回值為ETIME。

6、試題最終程式碼

通過前面的介紹,我們可以輕鬆的寫出程式碼了,如下所示:

[cpp] view plaincopyprint?
  1. /* 
  2.  是否熟悉POSIX多執行緒程式設計技術?如熟悉,編寫程式完成如下功能: 
  3.   1)有一int型全域性變數g_Flag初始值為0; 
  4.   2)在主線稱中起動執行緒1,列印“this is thread1”,並將g_Flag設定為1 
  5.   3)在主線稱中啟動執行緒2,列印“this is thread2”,並將g_Flag設定為2 
  6.   4)執行緒序1需要線上程2退出後才能退出 
  7.   5)主執行緒在檢測到g_Flag從1變為2,或者從2變為1的時候退出 
  8.    */
  9. #include<stdio.h>
  10. #include<stdlib.h>
  11. #include<pthread.h>
  12. #include<errno.h>
  13. #include<unistd.h>
  14. typedefvoid* (*fun)(void*);  
  15. int g_Flag=0;  
  16. static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  17. static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  
  18. void* thread1(void*);  
  19. void* thread2(void*);  
  20. /* 
  21.  *  when program is started, a single thread is created, called the initial thread or main thread. 
  22.  *  Additional threads are created by pthread_create. 
  23.  *  So we just need to create two thread in main(). 
  24.  */
  25. int main(int argc, char** argv)  
  26. {  
  27.     printf("enter main\n");  
  28.     pthread_t tid1, tid2;  
  29. int rc1=0, rc2=0;  
  30.     rc2 = pthread_create(&tid2, NULL, thread2, NULL);  
  31. if(rc2 != 0)  
  32.         printf("%s: %d\n",__func__, strerror(rc2));  
  33.     rc1 = pthread_create(&tid1, NULL, thread1, &tid2);  
  34. if(rc1 != 0)  
  35.         printf("%s: %d\n",__func__, strerror(rc1));  
  36.     pthread_cond_wait(&cond, &mutex);  
  37.     printf("leave main\n");  
  38.     exit(0);      
  39. }  
  40. /* 
  41.  * thread1() will be execute by thread1, after pthread_create() 
  42.  * it will set g_Flag = 1; 
  43.  */
  44. void* thread1(void* arg)  
  45. {  
  46.     printf("enter thread1\n");  
  47.     printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  48.     pthread_mutex_lock(&mutex);  
  49. if(g_Flag == 2)  
  50.         pthread_cond_signal(&cond);  
  51.     g_Flag = 1;  
  52.     printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  53.     pthread_mutex_unlock(&mutex);  
  54.     pthread_join(*(pthread_t*)arg, NULL);  
  55.     printf("leave thread1\n");  
  56.     pthread_exit(0);  
  57. }  
  58. /* 
  59.  * thread2() will be execute by thread2, after pthread_create() 
  60.  * it will set g_Flag = 2; 
  61.  */
  62. void* thread2(void* arg)  
  63. {  
  64.     printf("enter thread2\n");  
  65.     printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  66.     pthread_mutex_lock(&mutex);  
  67. if(g_Flag == 1)  
  68.         pthread_cond_signal(&cond);  
  69.     g_Flag = 2;  
  70.     printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());  
  71.     pthread_mutex_unlock(&mutex);  
  72.     printf("leave thread2\n");  
  73.     pthread_exit(0);  
  74. }  
  75. 編譯執行可以得到符合要求的結果  
/*
 是否熟悉POSIX多執行緒程式設計技術?如熟悉,編寫程式完成如下功能:
  1)有一int型全域性變數g_Flag初始值為0;
  2)在主線稱中起動執行緒1,列印“this is thread1”,並將g_Flag設定為1
  3)在主線稱中啟動執行緒2,列印“this is thread2”,並將g_Flag設定為2
  4)執行緒序1需要線上程2退出後才能退出
  5)主執行緒在檢測到g_Flag從1變為2,或者從2變為1的時候退出
   */
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>

typedef void* (*fun)(void*);

int g_Flag=0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* thread1(void*);
void* thread2(void*);

/*
 *  when program is started, a single thread is created, called the initial thread or main thread.
 *  Additional threads are created by pthread_create.
 *  So we just need to create two thread in main().
 */

int main(int argc, char** argv)
{
	printf("enter main\n");
	pthread_t tid1, tid2;
	int rc1=0, rc2=0;
	rc2 = pthread_create(&tid2, NULL, thread2, NULL);
	if(rc2 != 0)
		printf("%s: %d\n",__func__, strerror(rc2));

	rc1 = pthread_create(&tid1, NULL, thread1, &tid2);
	if(rc1 != 0)
		printf("%s: %d\n",__func__, strerror(rc1));

	pthread_cond_wait(&cond, &mutex);
	printf("leave main\n");
	exit(0);	
}

/*
 * thread1() will be execute by thread1, after pthread_create()
 * it will set g_Flag = 1;
 */
void* thread1(void* arg)
{
	printf("enter thread1\n");
	printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	pthread_mutex_lock(&mutex);
	if(g_Flag == 2)
		pthread_cond_signal(&cond);
	g_Flag = 1;
	printf("this is thread1, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	pthread_mutex_unlock(&mutex);
	pthread_join(*(pthread_t*)arg, NULL);
	printf("leave thread1\n");
	pthread_exit(0);
}

/*
 * thread2() will be execute by thread2, after pthread_create()
 * it will set g_Flag = 2;
 */
void* thread2(void* arg)
{
	printf("enter thread2\n");
	printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	pthread_mutex_lock(&mutex);
	if(g_Flag == 1)
		pthread_cond_signal(&cond);
	g_Flag = 2;
	printf("this is thread2, g_Flag: %d, thread id is %u\n",g_Flag, (unsigned int)pthread_self());
	pthread_mutex_unlock(&mutex);
	printf("leave thread2\n");
	pthread_exit(0);
}

編譯執行可以得到符合要求的結果

相關推薦

linux執行程式設計——同步互斥

我們在前面文章中已經分析了多執行緒VS多程序,也分析了執行緒的使用,現在我們來講解一下linux多執行緒程式設計之同步與互斥。 現在,我們不管究竟是多執行緒好還是多程序好,先講解一下,為什麼要使用多執行緒? 一、 為什麼要用多執行緒技術? 1、避免阻塞,大家知道,單個程序只

linux執行程式設計 同步互斥

——本文一個例子展開,介紹Linux下面執行緒的操作、多執行緒的同步和互斥。 前言 執行緒?為什麼有了程序還需要執行緒呢,他們有什麼區別?使用執行緒有什麼優勢呢?還有多執行緒程式設計的一些細節問題,如執行緒之間怎樣同步、互斥,這些東西將在本文中介紹。我在某QQ群裡見到這樣一道面試題: 是否熟悉POSIX多

Linux執行同步互斥的區別

同步與互斥這兩個概念經常被混淆,所以在這裡說一下它們的區別。 一、同步與互斥的區別 1. 同步 同步,又稱直接制約關係,是指多個執行緒(或程序)為了合作完成任務,必須嚴格按照規定的 某種先後次序來執行。 例如,執行緒 T2 中的語句 y 要使用執行緒

Linux — 淺析執行以及執行同步互斥

                   淺析多執行緒以及多執行緒的同步與互斥 執行緒概念 什麼是執行緒? 執行緒是程序內部的一個執行分支,執行緒的建立成本小於程序.Linux下沒有

Linux執行安全-同步互斥

執行緒安全:多個執行緒執行流對臨界資源的不安全爭搶操作 實現:如何讓執行緒之間安全對臨界資源進行操作就是同步與互斥 互斥:同一時間臨界資源的唯一訪問性 mutex(互斥量) ⼤部分情況,執行緒使⽤的資料都是區域性變數,變數的地址空間線上程棧空間內,這種情況,變數歸屬單

Linux執行同步互斥、生產者消費模型和讀者寫者問題、死鎖問題

執行緒的同步與互斥 執行緒是一個存在程序中的一個執行控制流,因為執行緒沒有程序的獨立性,在程序內部執行緒的大部分資源資料都是共享的,所以在使用的過程中就需要考慮到執行緒的安全和資料的可靠。不能因為執行緒之間資源的競爭而導致資料發生錯亂,也不能因為有些執行緒因為

Linux下的執行程式設計二(執行同步互斥

一、什麼叫做執行緒的同步與互斥?為什麼需要同步與互斥? 1、同步與互斥 互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。 同步:是指在互斥的基礎上(大多數情況),通過其它機制

Java執行程式設計執行同步互斥/執行安全/Java鎖

摘要:多執行緒三個特徵:原子性、可見性以及有序性.&gt;執行緒的同步與互斥?(同步執行緒與非同步執行緒,執行緒同步和非同步問題)&nbsp;&nbsp;1.同步:假設現有執行緒A和執行緒B,執行緒A需要往緩衝區寫資料,執行緒B需要從緩衝區讀資料,但他們之間存在一種制約

Linux執行程式設計---執行同步互斥鎖、條件變數、訊號量和讀寫鎖)

本篇博文轉自http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數、訊號量和讀寫鎖。  下面是思維導

Linux執行學習(2)--執行同步互斥及死鎖問題(互斥量和條件變數)

Linux多執行緒學習總結 一.互斥量 1.名詞理解 2.什麼是互斥量(mutex) 3.互斥量的介面 3.1 初始化訊號量 3.2 銷燬訊號量 3.3 互斥量加鎖和解鎖

Linux利用訊號量實現執行同步互斥

執行緒使用互斥鎖可以實現執行緒間的互斥,而互斥鎖本身就是對資源的一種標識狀態,當可以申請到鎖時說明此時資源可以使用,當申請鎖失敗時說明資源此時被其他執行緒所佔用不可使用,我們可以使用訊號量來代替互斥鎖實現。 訊號量用來表示資源數目,當一個執行緒要去訪問資源時,必須先去申請

Linux執行同步機制 .linux執行程式設計機制

#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex; pthread_cond_t  cond; void * child1(void *arg) {      

linux執行程式設計之一執行資料同步及相關api使用示例

多執行緒的使用在編碼過程中非常常見,如果快速的理解和掌握linux下的多執行緒程式設計呢?下文即將為您揭曉。一.linux多執行緒基本的建立及相關API使用: 1.執行緒的建立: int pthread_create(pthread_t thread, const pthread_attr_t attr,

python執行程式設計(3): 使用互斥同步執行

問題的提出 上一節的例子中,每個執行緒互相獨立,相互之間沒有任何關係。現在假設這樣一個例子:有一個全域性的計數num,每個執行緒獲取這個全域性的計數,根據num進行一些處理,然後將num加1。很容易寫出這樣的程式碼: # encoding: UTF-8import

linux執行程式設計互斥

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <pthread.h>

介紹Linux下面執行的操作、執行同步互斥

 執行緒?為什麼有了程序還需要執行緒呢,他們有什麼區別?使用執行緒有什麼優勢呢?還有多執行緒程式設計的一些細節問題,如執行緒之間怎樣同步、互斥,這些東西將在本文中介紹。我在某QQ群裡見到這樣一道面試題: 是否熟悉POSIX多執行緒程式設計技術?如熟悉,編寫程式完成如下功能

[領卓教育]執行同步互斥機制——訊號量

訊號量的初始化 int sem_init(sem_t *sem, int pshared, unsigned int value); 功能: 初始化訊號量 引數: sem :要是初始化的訊號量  pshared: 訊號量共享的範圍(0: 執行緒間使用 非0:程序間使用) value : 初始

[Xcode10 實際操作]八、網路執行-(23)執行同步非同步的區別

本文將演示執行緒的同步與非同步的區別。 在專案導航區,開啟檢視控制器的程式碼檔案【ViewController.swift】 非同步執行緒的執行,是沒有按照順序執行的。 1 import UIKit 2 3 class ViewController: UIViewController

linux執行程式設計,用 pthread_cond_timedwait 代替sleep

摘要:多執行緒程式設計中,執行緒A迴圈計算,然後sleep一會接著計算(目的是減少CPU利用率);存在的問題是,如果要關閉程式,通常選擇join執行緒A等待執行緒A退出,可是我們必須等到sleep函式返回,該執行緒A才能正常退出,這無疑減慢了程式退出的速度。當然,你可以terminate執行緒A,但

水滴石穿--執行安全同步

什麼是執行緒安全? 當多個執行緒訪問某一個類(物件或方法)時,這個物件始終都能表現出正確的行為, 那麼這個類(物件或方法)就是執行緒安全的。 java記憶體模型  在說明多執行緒安全問題前,要明白java記憶體模型。 為什麼有執行緒安全問題? 當多個執行緒同時共享