linux應用編程之進程間同步


一、描述

      在操作系統中,異步並發執行環境下的一組進程,因為相互制約關系,進而互相發送消息、互相合作、互相等待,使得各進程按一定的順序和速度執行,稱為進程間的同步。具有同步關系的一組並發進程,稱為合作進程,合作進程間互相發送的信號,稱為消息或事件。

      這種需要進程間同步的情況,是可以想見的,例如幾個進程訪問“臨界資源”。而為了解決進程間的同步問題,引入信號量的概念。

二、異步執行

  所謂異步執行命令,就是說一個線程用於接收解析命令,另外一個線程用於實際執行命令。實際工程中,經常會遇到有許多種命令要在一個線程中得到解析並執行,有些命令耗時短,可以在這個線程中完成;但是,有些命令耗時長,如果也放在這個線程中,則影響該線程接收(其他命令)。所以,此時可以考慮用異步執行的方案,將耗時短的命令,就放在接收解析線程中;而將耗時長的命令,則用異步執行的方案,將接收與實際執行分離,以避免接收線程受到嚴重阻塞。

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線程的信號量同步 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM