前面说的互斥量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;
}
运行结果如下所示: