四種進程或線程同步互斥的控制方法:
1、臨界區:通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。
2、互斥量:為協調共同對一個共享資源的單獨訪問而設計的。
3、信號量:為控制一個具有有限數量用戶資源而設計。
4、事件: 用來通知線程有一些事件已發生,從而啟動后繼任務的開始。
臨界區(Critical Section)(同一個進程內,實現互斥,但,無法實現同步,因為其具有“線程所有權”)
#include "stdafx.h" #include <stdio.h> #include <process.h> #include <windows.h> long g_nNum; //全局變量 unsigned int __stdcall Fun(void *pPM); //線程函數 const int THREAD_NUM = 10; //子線程個數 CRITICAL_SECTION g_cs1; CRITICAL_SECTION g_cs2; int _tmain(int argc, _TCHAR* argv[]) { g_nNum = 0; HANDLE handle[THREAD_NUM]; InitializeCriticalSection(&g_cs1); InitializeCriticalSection(&g_cs2); int i = 0; while (i < THREAD_NUM) { EnterCriticalSection(&g_cs1); handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); i++;//等子線程接收到參數時主線程可能改變了這個i的值 } //保證子線程已全部運行結束 WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); DeleteCriticalSection(&g_cs1); DeleteCriticalSection(&g_cs2); system("pause"); return 0; } unsigned int __stdcall Fun(void *pPM) { //由於創建線程是要一定的開銷的,所以新線程並不能第一時間執行到這來 int nThreadNum = *(int *)pPM; //子線程獲取參數 LeaveCriticalSection(&g_cs1); Sleep(50);//some work should to do EnterCriticalSection(&g_cs2); g_nNum++; //處理全局資源 Sleep(0);//some work should to do printf("線程編號為%d 全局資源值為%d\n", nThreadNum, g_nNum); LeaveCriticalSection(&g_cs2); return 0; }
運行結果(不同的計算機結果可能有所差異)截屏:
如果將_tmain函數中的while循環修改為如下:
while (i < THREAD_NUM) { EnterCriticalSection(&g_cs1); handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); i++;//等子線程接收到參數時主線程可能改變了這個i的值 Sleep(50);//新添加代碼 }
則可實現多個線程之間的互斥,且可使多線程順序啟動,運行結果截屏:
保證在某一時刻只有一個線程能訪問數據的簡便辦法。在任意時刻只允許一個線程對共享資源進行訪問。如果有多個線程試圖同時訪問臨界區,那么在有一個線程進入后其他所有試圖訪問此臨界區的線程將被掛起,並一直持續到進入臨界區的線程離開。臨界區在被釋放后,其他線程可以繼續搶占,並以此達到用原子方式操作共享資源的目的。
可以將臨界區比作旅館的房卡,調用EnterCriticalSection()即申請房卡,得到房卡后自己當然是可以多次進出房間的,在你調用LeaveCriticalSection()交出房卡之前,別人自然是無法進入該房間,在經典線程同步問題上,主線程正是由於擁有“線程所有權”即房卡,所以它可以重復進入關鍵代碼區域從而導致子線程在接收參數之前主線程就已經修改了這個參數。所以關鍵段可以用於線程間的互斥,但不可以用於同步。
互斥量(Mutex)(可以跨進程,實現互斥)
互斥量跟臨界區很相似,只有擁有互斥對象的線程才具有訪問資源的權限,由於互斥對象只有一個,因此就決定了任何情況下此共享資源都不會同時被多個線程所訪問。當前占據資源的線程在任務處理完后應將擁有的互斥對象交出,以便其他線程在獲得后得以訪問資源。互斥量比臨界區復雜。因為使用互斥不僅僅能夠在同一應用程序不同線程中實現資源的安全共享,而且可以在不同應用程序的線程之間實現對資源的安全共享。
互斥量與臨界區的作用非常相似,但互斥量是可以命名的,也就是說它可以跨越進程使用。所以創建互斥量需要的資源更多,所以如果只為了在進程內部是用的話使用臨界區會帶來速度上的優勢並能夠減少資源占用量。
信號量(Semaphores)(主要是實現同步,可以跨進程)
信號量對象對線程的同步方式與前面幾種方法不同,信號允許多個線程同時使用共享資源,這與操作系統中的PV操作相同。它指出了同時訪問共享資源的線程最大數目。它允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程數目。一般是將當前可用資源計數設置為最大資源計數,每增加一個線程對共享資源的訪問,當前可用資源計數就會減1,只要當前可用資源計數是大於0的,就可以發出信號量信號。但是當前可用計數減小到0時則說明當前占用資源的線程數已經達到了所允許的最大數目,不能在允許其他線程的進入,此時的信號量信號將無法發出
事件(Event)(實現同步,可以跨進程)
事件對象也可以通過通知操作的方式來保持線程的同步。並且可以實現不同進程中的線程同步操作。