進程間通訊簡單的說有三個問題,第一個問題是:一個進程如何把信息傳遞給另外一個,第二個問題是:要確保兩個或者更多的進程在互動中不會出現交叉(便是進程互斥問題),第三個問題是:進程間同步問題、ios
四種進程或者線程同步互斥的控制方法編程
1):臨界區:經過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。windows
2):互斥量:爲協調共同對一個共享資源的單獨訪問而設計的數組
3):信號量:爲控制一個具備有限數量用戶資源而設計安全
4):事件:用來通知線程有一些事件已發生,從而啓動後繼任務的開始。多線程
臨界區(Critical Section)併發
保證在某一時刻只有一個線程能訪問數據的簡便辦法,在任意時刻只容許一個線程對共享資源進行訪問,若是有多個線程試圖同時訪問臨界區,那麼在有一個線程進入後其餘試圖訪問此臨界區的線程將被掛起,並一直持續到進入臨界區的線程離開,臨界區在被釋放後,其餘線程能夠繼續搶佔,並以此達到原子方式操做共享資源的目的。函數
臨界區包含兩個操做原語:post
EnterCriticalSection() 進入臨界區
LeaveCriticalSection() 離開臨界區
雖然臨界區同步速度很快,但卻只能用來同步本進程內的線程,而不可用來同步多個進程中的線程。ui
互斥量(Mutex)
互斥量跟臨界區很類似,只有擁有互斥量對象的線程才具備訪問資源的權限,因爲互斥對象只有一個,所以就決定了任何狀況下此共享資源都不會同時被多個線程所訪問。當前佔據資源的線程在任務處理完後應將擁有的互斥對象交出,以便其它線程在得到以訪問資源,互斥量比臨界區複雜。使用互斥量不只僅能夠在同一進程的不一樣線程中實現資源的共享,並且還能夠在不一樣進程的線程之間實現對資源的安全訪問。
互斥量包含的幾個操做原語:
CreateMutex() 建立一個互斥量
OpenMutex() 打開一個互斥量
ReleaseMutex() 釋放互斥量
WaitForMultipleObjects() 等待互斥量對象
信號量(Semaphores)
信號量對象對線程的同步方式與上面的兩種方法不一樣,信號容許多個線程同時使用共享資源,這與操做系統中的pv操做相同,它指出了同時訪問共享資源的線程最大數目,它容許多個線程在同一時刻訪問同一資源,可是須要限制在同一時刻訪問此資源的最大線程數目。在用CreateSemaphone()建立信號量時既要同時指出容許的最大資源計數和當前可用資源計數,通常是將擋牆可用資源計數設置爲最大資源計數,每增長一個線程對共享資源的訪問,當前可用資源計數就會減去1,只要當前可用資源計數是大於0,就能夠發出信號量信號,可是當前可用計數減少到0則說明當前佔用資源的線程數已經達到了所容許的最大數目,不能再容許其餘線程的進入,此時的信號量將沒法發出,線程在處理完共享資源後,應在離開的同時經過ReleaseSemaphone()函數將當前可用資源計數加1,在任什麼時候候當前可用資源計數決不能大於最大資源計數。
信號量S是一個整數,S大於等於零時表明可供併發進程使用的資源實體數,但s小於零時則表示正在等待使用共享資源的進程數。
p操做申請資源:
1)s減去1
2)若s減1仍大於0,則進程繼續執行。
3)若s減1小於零,則該進程被阻塞後進入與該信號相對應的隊列中,而後轉入進程調度。
V操做釋放資源
1)s加1
2)若相加結果大於零,則進程繼續執行。
3)若相加結果小於等於零,則從該信號的等待隊列中喚醒一個等待進程,而後再返回原進程繼續執行或轉入進程調度、
信號量包含的幾個操做原語:
CreateSemaphore() 建立一個信號量
OpenSemaphore() 打開一個信號量
ReleaseSemaphore() 釋放信號量
WaitForSingleObject() 等待信號量
事件(Event)
事件對象也能夠經過通知操做的方式來保持線程的同步。而且能夠實現不一樣進程中的線程同步操做。
信號量包含的幾個操做原語:
CreateEvent() 建立一個事件
OpenEvent() 打開一個事件
SetEvent() 回置事件
WaitForSingleObject() 等待一個事件
WaitForMultipleObjects() 等待多個事件
WaitForMultipleObjects 函數原型:
WaitForMultipleObjects(
IN DWORD nCount, // 等待句柄數
IN CONST HANDLE *lpHandles, //指向句柄數組
IN BOOL bWaitAll, //是否徹底等待標誌
IN DWORD dwMilliseconds //等待時間
)
參 數nCount指定了要等待的內核對象的數目,存放這些內核對象的數組由lpHandles來指向。fWaitAll對指定的這nCount個內核對象的兩種等待方式進行了指定,爲TRUE時當全部對象都被通知時函數纔會返回,爲FALSE則只要其中任何一個獲得通知就能夠返回。 dwMilliseconds在這裏的做用與在WaitForSingleObject()中的做用是徹底一致的。若是等待超時,函數將返回 WAIT_TIMEOUT
信號量是一種解決進程同步的解決方案,同時信號量也能夠當作互斥量來使用,使用信號量來解決進程同步問題是編程中使用較多的方法。
使用信號量比較經典的問題時生產者消費者問題:下面舉個例子說明
因爲本例是在windows下寫的代碼,在windows下使用多線程編程,須要包含pthread.h.在ftp://sourceware.org/pub/pthreads-win32網址下、
我下載的是pthreads-w32-2-7-0-release.exe.雙擊pthreads-w32-2-7-0-release.exe,點擊Browse選擇安裝到的目錄,而後點擊Extract解壓,完成後點擊Done。
以後會在安裝目錄看到有三個文件夾Pre-built.二、pthreads.二、QueueUserAPCEx.
將Pre-built.2文件夾下的include和lib文件夾裏的文件複製到VS對應的include和lib目錄,我這裏是C:\Program Files\Microsoft Visual Studio 11.0\VC\include和C:\Program Files\Microsoft VisualStudio 11.0\VC\lib.這樣就能夠了,若是運行的時候連接錯誤,把pthreadVC2.dll 拷貝到你程序的可執行目錄下試試。
1 #include "stdafx.h" 2 #include <stdio.h> 3 #include <pthread.h> 4 #include <sched.h> 5 #include <semaphore.h> 6 #include <conio.h> 7 #include <ctype.h> 8 #include <signal.h> 9 #include <iostream> 10 11 #include<Windows.h> 12 using namespace std; 13 #pragma comment(lib,"pthreadVC2.lib") 14 15 #define N 5 //消費者或者生產者的數目 16 #define M 10 //緩衝數目 17 18 int productin = 0; //生產者放置產品的位置 19 int prochaseout = 0; //消費者取產品的位置 20 21 int buff[M] = {0}; //緩衝區初始化爲0,開始時沒有產品。 22 23 sem_t empty_sem; // 同步信號量,當滿的時候阻止生產者放產品。 24 sem_t full_sem; //同步信號量,當沒有產品的時候阻止消費者消費。 25 26 pthread_mutex_t mutex; //互斥信號量,一次只有一個線程訪問緩衝區。 27 28 int product_id = 0; //生產者id 29 int prochase_id = 0; //消費者id 30 31 void SignalExit(int signo) 32 { 33 printf("程序退出%d\n",signo); 34 return; 35 } 36 37 void PrintProduction() 38 { 39 printf("此時的產品隊列爲::"); 40 for(int i = 0; i < M; i++ ) 41 { 42 printf("%d ",buff[i]); 43 } 44 printf("\n\n"); 45 } 46 47 //////////////////////生產者方法//////////////////// 48 void* Product(void* pramter) 49 { 50 int id = ++product_id; 51 while(1) 52 { 53 Sleep(5000); //毫秒 54 sem_wait(&empty_sem); //給信號量減1操做 55 pthread_mutex_lock(&mutex); 56 productin = productin % M; 57 printf("生產者%d在產品隊列中放入第%d個產品\n\n",id,productin+1); 58 buff[productin] = 1; 59 PrintProduction(); 60 ++productin; 61 62 pthread_mutex_unlock(&mutex); //釋放互斥量對象 63 sem_post(&full_sem); //給信號量的值加1操做 64 } 65 } 66 67 //////////////消費者方法/////////////////////// 68 void* Prochase( void* pramter ) 69 { 70 int id = ++prochase_id; 71 while(1) 72 { 73 Sleep(7000); 74 sem_wait(&full_sem); 75 pthread_mutex_lock(&mutex); 76 prochaseout = prochaseout % M; 77 printf("消費者%d從產品隊列中取出第%d個產品\n\n",id,prochaseout+1); 78 79 buff[prochaseout] = 0; 80 PrintProduction(); 81 ++prochaseout; 82 83 pthread_mutex_unlock(&mutex); 84 sem_post(&empty_sem); 85 } 86 } 87 88 int main() 89 { 90 cout << "生產者和消費者數目都爲5,產品緩衝區爲10,生產者每2秒生產一個產品,消費者每5秒消費一個產品" << endl << endl; 91 pthread_t productid[N]; 92 pthread_t prochaseid[N]; 93 94 int ret[N]; 95 96 //初始化信號量 97 int seminit1 = sem_init(&empty_sem,0,M); 98 int seminit2 = sem_init(&full_sem,0,0); 99 if( seminit1 != 0 && seminit2 != 0 ) 100 { 101 printf("sem_init failed !!!\n"); 102 return 0; 103 } 104 105 //初始化互斥信號量 106 int mutexinit = pthread_mutex_init(&mutex,NULL); 107 if( mutexinit != 0 ) 108 { 109 printf("pthread_mutex_init failed !!\n"); 110 return 0; 111 } 112 113 //建立n個生產者線程 114 for(int i = 0; i < N; i++ ) 115 { 116 ret[i] = pthread_create( &productid[i], NULL,Product,(void*)(&i) ); 117 if( ret[i] != 0 ) 118 { 119 printf("生產者%d線程建立失敗!\n",i); 120 return 0; 121 } 122 } 123 124 //建立n個消費者線程 125 for(int j = 0; j < N; j++ ) 126 { 127 ret[j] = pthread_create(&prochaseid[j],NULL,Prochase,NULL); 128 if( ret[j] != 0 ) 129 { 130 printf("消費者%d線程建立失敗\n",j); 131 return 0; 132 } 133 } 134 135 ///////////////////////等待線程被銷燬/////////////////////////////////////////////// 136 for( int k = 0; k < N; k++ ) 137 { 138 printf("銷燬線程\n"); 139 pthread_join(productid[k],NULL); 140 pthread_join(prochaseid[k],NULL); 141 } 142 return 0; 143 }