`

经典线程同步 事件Event

 
阅读更多

Event 内核对象,实际上是解决线程同步问题的利器。

介绍下函数应用:

 

第一个 CreateEvent

函数功能:创建事件

函数原型:

HANDLECreateEvent(

 LPSECURITY_ATTRIBUTES lpEventAttributes,

 BOOL bManualReset,

 BOOL bInitialState,

 LPCTSTR lpName

);

函数说明:

第一个参数表示安全控制,一般直接传入NULL。

第二个参数确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。

如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。打个小小比方,手动置位事件相当于教室门,教室门一旦打开(被触发),所以有人都可以进入直到老师去关上教室门(事件变成未触发)。自动置位事件就相当于医院里拍X光的房间门,门打开后只能进入一个人,这个人进去后会将门关上,其它人不能进入除非门重新被打开(事件重新被触发)。

如果是手动置位,则需要自己调用ResetEvent()使事件变成未触发状态。

第三个参数表示事件的初始状态,传入TRUR表示已触发。

第四个参数表示事件的名称,传入NULL表示匿名事件。

 

第二个 OpenEvent

函数功能:根据名称获得一个事件句柄。

函数原型:

HANDLEOpenEvent(

 DWORD dwDesiredAccess,

 BOOL bInheritHandle,

 LPCTSTR lpName     //名称

);

函数说明:

第一个参数表示访问权限,对事件一般传入EVENT_ALL_ACCESS。详细解释可以查看MSDN文档。

第二个参数表示事件句柄继承性,一般传入TRUE即可。

第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个事件。

 

第三个SetEvent

函数功能:触发事件

函数原型:BOOL SetEvent(HANDLE hEvent);

函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。

 

第四个ResetEvent

函数功能:将事件设为末触发

函数原型:BOOL ResetEvent(HANDLE hEvent);

 

最后一个事件的清理与销毁

由于事件是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。

 

在经典多线程问题中设置一个事件和一个关键段。用事件处理主线程与子线程的同步,用关键段来处理各子线程间的互斥。代码如下:

#include <windows.h>
#include <process.h>
#include <iostream>

using namespace std;

const int THREADNUM = 20;
volatile long number = 0;
HANDLE th_event;

CRITICAL_SECTION threadPM,threadCode;

unsigned int __stdcall threadFunc(PVOID pM) {
	int nThreadNum = * ((int *)pM);
	SetEvent(th_event);
	Sleep(100);
	EnterCriticalSection(&threadCode);
	cout << nThreadNum << endl;
	number++;
	Sleep(0);
	LeaveCriticalSection(&threadCode);
	return 0;
}

int main() {
	int num = 20;
	HANDLE handle[THREADNUM];
	InitializeCriticalSection(&threadCode);
	number = 0;
	th_event = CreateEvent(NULL, TRUE, FALSE, NULL);
	for(int i=0; i< THREADNUM; i++) {
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, threadFunc, (PVOID) &i, 0, NULL);
		WaitForSingleObject(th_event, INFINITE);
		ResetEvent(th_event);
	}
	
	WaitForMultipleObjects(THREADNUM, handle, TRUE ,INFINITE); //安全数量为64
	Sleep(500);
	cout << "计数个数为" << number << endl;
	
	CloseHandle(th_event);
	DeleteCriticalSection(&threadCode);
	getchar();
	return 0;
}

 

上述代码是事件对象的手动置位,

下面的两行尤为重要:


保证主线程和子线程之间可以同步访问。

运行效果如下:


 

如果在手动条件下,忘记对事件重新置位,即忘记写resetevent函数,则同步将没有效果,运行如下:


 

在自动置位的条件下,则不用书写resetEvent函数,将TRUE改为FALSE即可。


运行效果如下:


正常。

 

 

接下来还有一个函数值得注意:

第五个PulseEvent

函数功能:将事件触发后立即将事件设置为未触发,相当于触发一个事件脉冲。

函数原型:BOOL PulseEvent(HANDLE hEvent);

函数说明:这是一个不常用的事件函数,此函数相当于SetEvent()后立即调用ResetEvent();此时情况可以分为两种:

 

1.对于手动置位事件,所有正处于等待状态下线程都变成可调度状态。

2.对于自动置位事件,所有正处于等待状态下线程只有一个变成可调度状态。

 

此后事件是末触发的。该函数不稳定,因为无法预知在调用PulseEvent ()时哪些线程正处于等待状态。

 

 

手动置位:代码如下

主线程,启动三个快线程和两个慢线程。

在慢线程创建好以后,故意停顿50毫秒,目的是为了让快线程全部进入对于事件的等待状态,对于事件脉冲有一个充分的感知。

主线程启动所有子线程后再Sleep(50)保证有3个快线程都正处于等待状态中。此时若主线程触发一个事件脉冲,那么对于手动置位事件,这3个线程都将顺利执行下去。对于自动置位事件,这3个线程中会有中一个顺利执行下去。而不论手动置位事件还是自动置位事件,那2个慢线程由于Sleep(100)所以会错过事件脉冲,因此慢线程都会进入等待状态而无法顺利执行下去。

#include <iostream>
#include <windows.h>
#include <process.h>

using namespace std;

HANDLE th_event;

unsigned int __stdcall faseThreadFunc(PVOID pm) {
	char* s = (char *) pm;
	cout << "进入快线程......" << endl;
	Sleep(10);
	WaitForSingleObject(th_event, INFINITE);
	cout << s << endl;
	return 0;
}

unsigned int __stdcall slowThreadFunc(PVOID pm) {
	char* s = (char *) pm;
	cout << "进入慢线程......" << endl;
	Sleep(100);
	WaitForSingleObject(th_event, INFINITE);
	cout << s << endl;
	return 0;
}

int main() {

	bool ev_Manual = true;

	if(ev_Manual) cout << "手动测试状态" << endl;
	else cout << "自动测试状态" << endl;

	HANDLE th_handle1[3];
	HANDLE th_handle2[2];
	char s1[3][10] = {"快线程1", "快线程2", "快线程3"};
	char s2[2][10] = {"慢线程1", "慢线程2"};
	int i;

	th_event = CreateEvent(NULL, ev_Manual, FALSE, NULL);

	for(i=0; i<3; i++) {
		th_handle1[i] = (HANDLE) _beginthreadex(NULL, 0, faseThreadFunc, (PVOID) s1[i], 0, NULL);
	}

	for(i=0; i<2; i++) {
		th_handle2[i] = (HANDLE) _beginthreadex(NULL, 0, slowThreadFunc, (PVOID) s2[i], 0, NULL);
	}

	Sleep(50);
	cout << "现在主线程触发一个事件脉冲" << endl;
	PulseEvent(th_event);

	getchar();
	return 0;
}

运行结果如下:


 

自动置位:

只是将ev_Manual 改为 false就好;

运行结果如下:


 

最后总结下事件Event

1.事件是内核对象,事件分为手动置位事件和自动置位事件。事件Event内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发。

2.事件可以由SetEvent()来触发,由ResetEvent()来设成未触发。还可以由PulseEvent()来发出一个事件脉冲。

3.事件可以解决线程间同步问题,因此也能解决互斥问题。

 

 

  • 大小: 916 Bytes
  • 大小: 15.9 KB
  • 大小: 14.2 KB
  • 大小: 2.8 KB
  • 大小: 14.4 KB
  • 大小: 13.4 KB
  • 大小: 13.4 KB
分享到:
评论

相关推荐

    多线程同步:事件(event)

    在给定的“多线程同步:事件(event)”主题中,我们将深入探讨如何利用事件来实现线程间的同步,以及如何控制线程访问资源的顺序。 首先,理解线程同步的概念至关重要。当多个线程试图同时访问和修改同一资源时,...

    Visual C++事件机制线程同步工程

    在Windows API中,提供了多种线程同步机制,如临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)以及事件(Event)等。 事件机制是一种通知机制,允许一个线程通知其他线程某个特定事件已经发生。...

    VC++ 2010 通过Event实现多线程同步

    多线程同步的基本思路是:当某个线程完成特定任务后,它会设置Event为信号状态,使得其他等待该事件的线程可以开始执行。这在处理共享资源时特别有用,可以避免数据竞争和死锁。 例如,我们有两个线程,一个负责...

    线程同步的五种方法

    在Windows编程中,提供了多种线程同步机制,包括互斥量、临界区、原子操作、事件以及信号量等。以下是对这些方法的详细解释: 1. **互斥量(Mutex)**:互斥量是一种独占式同步对象,一次只有一个线程可以拥有互斥...

    [Delphi]多线程编程(13)多线程同步之Event(事件对象).pdf

    标题《[Delphi]多线程编程(13)多线程同步之Event(事件对象)》意味着这份文档关注于Delphi编程语言中处理多线程时如何使用事件对象进行线程间的同步。在多线程编程中,事件(Event)是一种同步机制,用于控制线程的...

    Windows多线程间同步事件的控制方法

    本文旨在详细介绍如何在Windows 95环境中使用`event`对象来控制多线程之间的同步事件。 #### 线程概念与创建方法 在Windows 95中,每个应用程序都作为一个进程运行,该进程可以创建多个并发执行的线程。线程是系统...

    利用事件实现多线程同步

    本主题将深入探讨如何利用事件(Event)来实现多线程同步。 事件(Event)是一种同步机制,它允许一个线程通过信号通知另一个线程某个特定的操作已经完成或者特定条件已经满足。在多线程编程中,事件常被用于解决生产者...

    vc++中的线程锁(线程锁保持线程同步)

    VC++中实现线程锁的方法多种多样,包括临界区(Critical Section)、互斥量(Mutex)、事件对象(Event)等。在提供的文件列表中,如`RWLock.cpp`,可能涉及到了读写锁(Read-Write Lock),这是一种更为复杂的线程...

    vc 多线程同步 事件

    事件(EVENT)是Windows API提供的一种线程同步机制,它允许线程间进行通信和协调,确保数据的一致性和避免竞态条件。在本文中,我们将深入探讨如何在VC++环境下使用事件进行多线程同步。 首先,我们需要了解什么是...

    线程同步的四种方式

    事件是Windows API提供的一种线程同步机制,它允许一个线程通知其他线程某个特定事件已经发生。事件对象有两种状态:有信号(Signaled)和无信号(Unsignaled)。当事件为有信号状态时,等待该事件的线程可以继续...

    VC++线程同步实例

    在VC++中,我们可以使用多种机制来实现线程同步,如临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)和事件(Event)等。在这个模拟中,我们可以将司机视为一个线程,售票员视为另一个线程,乘客则作为一...

    线程间同步和通信之事件(动态)

    它提供了丰富的线程管理、内存管理、信号量、互斥锁、邮箱、消息队列等机制,其中事件(Event)是一种高效且灵活的同步和通信方式。 事件机制允许线程之间共享一组标志位,每个标志位代表一个特定的事件。线程可以...

    多线程及线程同步

    本文将详细介绍如何通过临界区、互斥内核对象、事件内核对象和信号量内核对象来实现线程同步。 1. 临界区(Critical Section) 临界区是一种简单的线程同步方法,用于保护共享资源免受并发访问。在进入临界区之前,...

    线程同步代码集实例

    在Windows API中,提供了多种线程同步机制,如Event、Mutex、Semaphore和Section。接下来,我们将详细讨论这些同步机制以及它们的应用实例。 1. Event同步:Event对象是一种信号量,可以用来通知线程有特定事件发生...

    易语言利用Event事件实现多线程暂停继续源码

    其中,Event事件是一个关键的概念,它是线程间通信的重要工具,可以用于同步线程操作,实现线程的等待和唤醒。 Event事件分为两种类型:自动重置事件(AutoResetEvent)和手动重置事件(ManualResetEvent)。自动...

    利用CEvent实现多线程同步.rar_CEvent_cevent example_event_线程同步

    在多线程编程中,线程同步是一种至关重要的技术,它确保了多个线程在访问共享资源时的正确性和一致性。C++标准库提供了一种工具——CEvent,用于实现线程间的同步通信。本教程将深入探讨如何利用CEvent在C++中实现多...

    VC多线程同步实例程序

    本实例程序“VC多线程同步实例程序”着重探讨了如何在Visual C++中创建线程以及实现线程间的同步。下面将详细解释这些概念和技术。 首先,我们需要理解什么是线程。线程是操作系统调度的基本单位,一个进程中可以...

    线程同步的三种方式 源码+说明

    在Windows系统中,有三种主要的线程同步机制:互斥量(Mutex)、事件对象(Event)和临界区(Critical Section)。以下是对这些机制的详细说明: 1. **互斥量(Mutex)** 互斥量是一种跨进程的线程同步工具,它...

    线程同步(4种方法).rar

    在C++和MFC(Microsoft Foundation Classes)框架下,我们可以使用多种方法实现线程同步,包括互斥量、信号量、事件等。下面将详细讲解这四种方法。 1. **互斥量(Mutex)** 互斥量是一种独占式同步机制,它允许只有...

    delphi中线程同步问题

    `Synchronize` 的实现基于事件和线程同步原语。当一个非主线程调用 `Synchronize` 时,它会创建一个同步记录结构,包含调用的线程引用、方法指针和异常处理信息。接着,这个结构会被放入一个全局的同步列表 `Sync...

Global site tag (gtag.js) - Google Analytics