一、描述
在操作系統中,異步並發執行環境下的一組進程,因為相互制約關系,進而互相發送消息、互相合作、互相等待,使得各進程按一定的順序和速度執行,稱為進程間的同步。具有同步關系的一組並發進程,稱為合作進程,合作進程間互相發送的信號,稱為消息或事件。
這種需要進程間同步的情況,是可以想見的,例如幾個進程訪問“臨界資源”。而為了解決進程間的同步問題,引入信號量的概念。
二、異步執行
所謂異步執行命令,就是說一個線程用於接收解析命令,另外一個線程用於實際執行命令。實際工程中,經常會遇到有許多種命令要在一個線程中得到解析並執行,有些命令耗時短,可以在這個線程中完成;但是,有些命令耗時長,如果也放在這個線程中,則影響該線程接收(其他命令)。所以,此時可以考慮用異步執行的方案,將耗時短的命令,就放在接收解析線程中;而將耗時長的命令,則用異步執行的方案,將接收與實際執行分離,以避免接收線程受到嚴重阻塞。
example:
本例程中,用主線程創建了兩個子線程pthread1和pthread2,其中線程pthread1用於產生命令(模仿接受解析過程),而線程pthread2用於實際執行命令。
代碼如下:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<pthread.h> 4 #include <semaphore.h> 5 6 /* 將信號量定義為全局變量,方便多個線程共享 */ 7 sem_t sem; 8 9 /* 線程1和線程2的公用命令 */ 10 int gCmd = 0; 11 12 /* 同步線程1和線程2的全局變量 */ 13 static int gIsExecFlag = 0; 14 15 /* 定義線程pthread1 */ 16 static void * pthread1(void *arg) 17 { 18 /* 線程pthread1開始運行 */ 19 printf("pthread1 start!\n"); 20 21 while(1) 22 { 23 /* 等待沒有命令正在執行 */ 24 while(gIsExecFlag); 25 26 /* 更新命令 */ 27 gCmd++; 28 if(gCmd == 10) 29 { 30 /* 釋放信號量 */ 31 sem_post(&sem); 32 33 /* 發送命令結束 */ 34 return NULL; 35 } 36 37 /* 釋放信號量 */ 38 sem_post(&sem); 39 40 /* 等待線程2執行命令 */ 41 sleep(1); 42 } 43 } 44 45 /* 定義線程pthread2 */ 46 static void * pthread2(void *arg) 47 { 48 int tmp; 49 50 /* 線程pthread2開始運行 */ 51 printf("pthread2 start!\n"); 52 53 while(1) 54 { 55 if (sem_wait(&sem) != 0) 56 { 57 printf("Error!\n"); 58 } 59 60 /* 正在執行的標志置1 */ 61 gIsExecFlag = 1; 62 63 /* 線程2接受來自線程1的命令,並打印 */ 64 tmp = gCmd; 65 printf("now execute the cmd,and the code of cmd is %d.\n", tmp); 66 67 /* 執行命令需要時間:3s,模仿實際命令執行 */ 68 sleep(3); 69 70 /* 正在執行的標志清0 */ 71 gIsExecFlag = 0; 72 73 if(gCmd == 10){ 74 /* 命令執行結束 */ 75 return NULL; 76 } 77 } 78 } 79 80 /* main函數 */ 81 int main(int agrc,char* argv[]) 82 { 83 pthread_t tidp1,tidp2; 84 85 /* 初始化信號量sem,注意初始值為0 */ 86 sem_init(&sem, 0, 0); 87 88 /* 創建線程pthread1 */ 89 if ((pthread_create(&tidp1, NULL, pthread1, NULL)) == -1) 90 { 91 printf("create error!\n"); 92 return 1; 93 } 94 95 /* 同步,讓線程1先執行 */ 96 usleep(10); 97 98 /* 創建線程pthread2 */ 99 if ((pthread_create(&tidp2, NULL, pthread2, NULL)) == -1) 100 { 101 printf("create error!\n"); 102 return 1; 103 } 104 105 /* 等待線程pthread1釋放 */ 106 if (pthread_join(tidp1, NULL)) 107 { 108 printf("thread is not exit...\n"); 109 return -2; 110 } 111 112 /* 等待線程pthread2釋放 */ 113 if (pthread_join(tidp2, NULL)) 114 { 115 printf("thread is not exit...\n"); 116 return -2; 117 } 118 119 return 0; 120 }
代碼重點解析:
進程pthread1和進程pthread2之間單純用信號量sem同步,無法解決發送線程pthread1,在線程pthread2正在執行命令時,又寫入了新的命令的問題,造成命令執行錯亂。為了解決這個問題,引入全局變量gIsExecFlag用於同步。經過信號量sem和全局變量gIsExecFlag的完美配合,就可以實現命令發送與執行過程的有序配合。
測試效果
編譯命令:
#arm-linux-gcc -o pthread pthread.c -lpthread
執行結果:
后續分析
由上圖可知,兩個線程的整體執行周期,並非是線程pthread1和線程pthread2周期的和,而是取兩者中的最大者。實際上,這也很容易想見,兩個線程的通信速度,取決於兩個線程中速度最慢者,也對應這個結論。
經過測試,不論發送線程和執行線程的速度孰大孰小,總體的執行結果是一樣的,都能保證命令執行流程的正確。所以,就可以證明上述代碼的可行性。但是,需要注意的是,線程之間的同步時間還是有限制的,線程pthread1的睡眠時間應≥10ms,否則將會出現執行流程的錯誤。
參考資料:Linux線程的信號量同步