windows多线程系列003 利用事件对象实现线程同步

这一篇还是讨论多线程的问题,在这一篇中我们使用事件对象来解决同样的问题(火车站售票),还是先看我们需要用到的三个函数的原型:

(1)CreateEvent()创建事件对象

HANDLE
WINAPI
CreateEvent(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
_In_ BOOL bManualReset,
_In_ BOOL bInitialState,
_In_opt_ LPCWSTR lpName
);
lpEventAttributes
一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
bManualReset
指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。
bInitialState
指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
lpName
指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。
如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。
如果lpName为NULL,将创建一个无名的事件对象。
如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。

(2)SetEvent()设置事件对象状态

BOOL
WINAPI
SetEvent(
_In_ HANDLE hEvent
);
hEvent
指定将要设置其状态的时间对象的句柄

(3)ResetEvent()重置事件对象状态

BOOL
WINAPI
ResetEvent(
_In_ HANDLE hEvent
);
hEvent
指定将要重置其状态的事件对象的句柄。
那么如何利用事件对象解决线程同步的问题呢?上代码:
#include <windows.h>
#include <iostream>
using namespace std;

DWORD WINAPI Fun31Proc(LPVOID lpParameter);
DWORD WINAPI Fun32Proc(LPVOID lpParameter);

//int index=0;
int tickets3 = 100;
HANDLE g_hEvent;

void main003(){

	HANDLE hThread1;
	HANDLE hThread2;

	//创建自动重置事件内核对象
	g_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
	//设置为有信号状态
	SetEvent(g_hEvent);

	//创建线程
	hThread1 = CreateThread(NULL, 0, Fun31Proc, NULL, 0, NULL);
	hThread2 = CreateThread(NULL, 0, Fun32Proc, NULL, 0, NULL);

	//关闭句柄,函数并没有终止新创建的线程,只是表示在主线程中对新创建的线程的引用不感兴趣,因此将它关闭
	CloseHandle(hThread1);
	CloseHandle(hThread2);

	//main 函数主线程等待4秒
	Sleep(1000);
	//关闭事件对象
	CloseHandle(g_hEvent);
	system("pause");
	return;
}

//线程1的入口函数
DWORD WINAPI Fun31Proc(LPVOID lpParameter){

	while (true)
	{
		//所请求的对象属于处于有信号状态,该函数才会返回,线程才能继续往下执行
		WaitForSingleObject(g_hEvent, INFINITE);
		ResetEvent(g_hEvent);

		if (tickets3 > 0){
			cout << "thread1 sell ticket" << tickets3-- << endl;
			//释放当前线程对互斥对象的所有权,让该对象处于已通知状态
			SetEvent(g_hEvent);
		}
		else{
			SetEvent(g_hEvent);
			break;
		}
	}
	return 0;
}

DWORD WINAPI Fun32Proc(LPVOID lpParameter){

	while (true)
	{
		//所请求的对象属于处于有信号状态,该函数才会返回,线程才能继续往下执行,注意调用WaitForSingleObject()的位置
		WaitForSingleObject(g_hEvent, INFINITE);
		ResetEvent(g_hEvent);
		if (tickets3 > 0){
			cout << "thread2 sell ticket" << tickets3-- << endl;
			//释放当前线程对互斥对象的所有权,让该对象处于已通知状态,注意调用WaitForSingleObject()的位置
			SetEvent(g_hEvent);
		}
		else{
			//释放当前线程对互斥对象的所有权,让该对象处于已通知状态,注意调用WaitForSingleObject()的位置
			SetEvent(g_hEvent);
			break;
		}
	}
	return 0;
}
    在这个事件对象处理多线程同步的问题中,我们需要注意:事件对象有人工重置事件对象和自动重置事件对象。当人工重置事件对象得到通知时,等待该事件的所有线程都变为可调度线程;而对于自动重置事件对象得到通知时,只有一个线程变为可调度的线程,同时操作系统会将该事件对象设置无信号状态,这样,当对所保护的代码执行完成后,需要调用SetEvent函数将该事件对象设置为有信号状态。而人工重置的事件对象,在一个线程得到该事件对象之后,操作系统并不会将该事件对象设置为无信号状态,除非显式地调用ResetEvent函数将其设置为无信号状态,否则该对象会一直是有信号状态。

你可能感兴趣的:(windows多线程系列003 利用事件对象实现线程同步)