前面說的互斥量Mutex與關鍵段CriticalSection都不能實現線程的同步,只能實現互斥,接下來我們用時間event就可以實現線程的同步了,事件也是一個內核對象。
一、相關函數說明
(一) 創建事件
1.函數原型
HANDLE WINAPI CreateEventW(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
_In_ BOOL bManualReset,
_In_ BOOL bInitialState,
_In_opt_ LPCWSTR lpName
);
2.參數說明
-
第一個參數表示安全控制,一般直接傳入NULL。
-
第二個參數確定事件是手動置位還是自動置位,傳入TRUE表示手動置位,傳入FALSE表示自動置位。如果為自動置位,則對該事件調用WaitForSingleObject()后會自動調用ResetEvent()使事件變成未觸發狀態。
-
第三個參數表示事件的初始狀態,傳入TRUR表示已觸發。
-
第四個參數表示事件的名稱,傳入NULL表示匿名事件。
(二) 打開事件
1.函數原型
HANDLE WINAPI OpenEventW(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCWSTR lpName
);
2.參數說明
-
第一個參數表示訪問權限,對事件一般傳入EVENT_ALL_ACCESS。
-
第二個參數表示事件句柄繼承性,一般傳入TRUE即可。
-
第三個參數表示名稱,不同進程中的各線程可以通過名稱來確保它們訪問同一個事件。
(三) 觸發事件
1.函數原型
BOOL WINAPI SetEvent(
_In_ HANDLE hEvent
);
2.參數說明
-
函數說明:每次觸發后,必有一個或多個處於等待狀態下的線程變成可調度狀態。
-
hEvent 為要觸發的事件的句柄(內核對象)
(四)、 將事件設為末觸發
1.函數原型
BOOL WINAPI ResetEvent(
_In_ HANDLE hEvent
);
2.參數說明
- hEvent 為要觸發的事件的句柄(內核對象)
二、實例
前面我們用關鍵段和互斥量無法實現線程同步,在前面的程序中,我們可以實現對全局資源互斥訪問,即每個線程給全局資源加一,現在使用事件可以時間線程同步,即實現每個線程按順序依次給全局資源加一,代碼如下:
//使用事件進行線程同步
#include<iostream>
#include <windows.h>
using namespace std;
CRITICAL_SECTION g_csVar;
HANDLE g_event;
const int THREAD_NUM = 10;
int g_Num = 0;
DWORD WINAPI Func(LPVOID);
int main()
{
g_event = CreateEvent(NULL, false, false, NULL); //初始化事件為未觸發狀態
InitializeCriticalSection(&g_csVar);
DWORD ThreadId[THREAD_NUM];
HANDLE handle[THREAD_NUM];
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = CreateThread(NULL, 0, Func, &i, 0, &ThreadId[i]);
WaitForSingleObject(g_event, INFINITE); //等待事件被觸發
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
CloseHandle(g_event);
DeleteCriticalSection(&g_csVar);
return 0;
}
DWORD WINAPI Func(LPVOID p)
{
int nThreadNum = *(int*)p;
EnterCriticalSection(&g_csVar);
cout << "線程編號為:" << nThreadNum << " 給全局資源g_Num 加1,現在給全局資源g_Num值為:" << ++g_Num << endl;
LeaveCriticalSection(&g_csVar);
SetEvent(g_event); //觸發事件
return 0;
}
運行結果如下所示: