秒殺多執行緒第八篇 經典執行緒同步 訊號量Semaphore
前面介紹了關鍵段CS、事件Event、互斥量Mutex在經典執行緒同步問題中的使用。本篇介紹用訊號量Semaphore來解決這個問題。
首先也來看看如何使用訊號量,訊號量Semaphore常用有三個函式,使用很方便。下面是這幾個函式的原型和使用說明。
第一個 CreateSemaphore
函式功能:建立訊號量
函式原型:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
函式說明:
第一個引數表示安全控制,一般直接傳入NULL。
第二個引數表示初始資源數量。
第三個引數表示最大併發數量。
第四個引數表示訊號量的名稱,傳入NULL表示匿名訊號量。
第二個 OpenSemaphore
函式功能:開啟訊號量
函式原型:
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函式說明:
第一個引數表示訪問許可權,對一般傳入SEMAPHORE_ALL_ACCESS。詳細解釋可以檢視MSDN文件。
第二個引數表示訊號量控制代碼繼承性,一般傳入TRUE即可。
第三個引數表示名稱,不同程序中的各執行緒可以通過名稱來確保它們訪問同一個訊號量。
第三個 ReleaseSemaphore
函式功能:遞增訊號量的當前資源計數
函式原型:
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
函式說明:
第一個引數是訊號量的控制代碼。
第二個引數表示增加個數,必須大於0且不超過最大資源數量。
第三個引數可以用來傳出先前的資源計數,設為NULL表示不需要傳出。
注意:當前資源數量大於0,表示訊號量處於觸發,等於0表示資源已經耗盡故訊號量處於末觸發。在對訊號量呼叫等待函式時,等待函式會檢查訊號量的當前資源計數,如果大於0(即訊號量處於觸發狀態),減1後返回讓呼叫執行緒繼續執行。一個執行緒可以多次呼叫等待函式來減小訊號量。
最後一個 訊號量的清理與銷燬
由於訊號量是核心物件,因此使用CloseHandle()就可以完成清理與銷燬了。
在經典多執行緒問題中設定一個訊號量和一個關鍵段。用訊號量處理主執行緒與子執行緒的同步,用關鍵段來處理各子執行緒間的互斥。詳見程式碼:
#include <stdio.h> #include <process.h> #include <windows.h> long g_nNum; unsigned int __stdcall Fun(void *pPM); const int THREAD_NUM = 10; //訊號量與關鍵段 HANDLE g_hThreadParameter; CRITICAL_SECTION g_csThreadCode; int main() { printf(" 經典執行緒同步 訊號量Semaphore\n"); printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n"); //初始化訊號量和關鍵段 g_hThreadParameter = CreateSemaphore(NULL, 0, 1, NULL);//當前0個資源,最大允許1個同時訪問 InitializeCriticalSection(&g_csThreadCode); HANDLE handle[THREAD_NUM]; g_nNum = 0; int i = 0; while (i < THREAD_NUM) { handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); WaitForSingleObject(g_hThreadParameter, INFINITE);//等待訊號量>0 ++i; } WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); //銷燬訊號量和關鍵段 DeleteCriticalSection(&g_csThreadCode); CloseHandle(g_hThreadParameter); for (i = 0; i < THREAD_NUM; i++) CloseHandle(handle[i]); return 0; } unsigned int __stdcall Fun(void *pPM) { int nThreadNum = *(int *)pPM; ReleaseSemaphore(g_hThreadParameter, 1, NULL);//訊號量++ Sleep(50);//some work should to do EnterCriticalSection(&g_csThreadCode); ++g_nNum; Sleep(0);//some work should to do printf("執行緒編號為%d 全域性資源值為%d\n", nThreadNum, g_nNum); LeaveCriticalSection(&g_csThreadCode); return 0; }
可以看出來,訊號量也可以解決執行緒之間的同步問題。 由於訊號量可以計算資源當前剩餘量並根據當前剩餘量與零比較來決定訊號量是處於觸發狀態或是未觸發狀態,因此訊號量的應用範圍相當廣泛。本系列的《秒殺多執行緒第十篇 生產者消費者問題》將再次使用它來解決執行緒同步問題,歡迎大家參閱。 至此,經典執行緒同步問題全部結束了,下一篇《秒殺多執行緒第九篇 經典多執行緒同步問題總結》將會對其作個總結以梳理各知識點。