Event(事件)的简单使用

本文深入探讨了事件对象在多线程程序中的两种主要用途:通知型和同步型。通过具体示例,展示了如何利用事件对象实现线程间的通信与同步,确保线程安全和高效的数据处理。

*2023/06/21*

   重新认识事件对象的使用

事件对象按用途分为两种:通知型和同步型。通知型的作用相当于一对多,一个线程可以通过通知型事件告诉多个其他等待该事件的线程我准备好了,或者类似的作用;而同步型事件则相当于一对一,一个时间点上只有一个线程可以占有该事件对象,其他线程需要等待,从而实现线程间的同步。以下示例,演示了这两种事件对象的使用:三个线程等待通知型事件对象,等到有信号之后,三个线程同步按顺序打印字符数组中的数据,每个线程打印不同类型的字符。代码如下:

#include<stdio.h>
#include<Windows.h>

HANDLE hNotificationEvent = INVALID_HANDLE_VALUE;
HANDLE hSynchronizationEvent = INVALID_HANDLE_VALUE;

char dataChar[MAX_PATH] = "jk12d3!@u#a$b7c";

enum char_type {
	char_num,
	char_char,
	char_spec
};

BOOLEAN checkType(char chr, int charType)
{
	BOOLEAN result = FALSE;
	//字符型
	//a~z
	if (charType == char_char)
	{
		if (
			chr >= 'a'
			&&chr <= 'z'
			)
		{
			result = TRUE;
		}
	}
	else if (charType == char_num)//数字型 0~9
	{
		if (
			chr >= '0'
			&& chr <= '9'
			)
		{
			result = TRUE;
		}
	}
	else if(charType==char_spec)//其他特殊字符
	{
		if (
			(chr >= 'a' &&chr<='z')
			|| (chr >='0' &&chr<='9')
			)
		{
			result = FALSE;
		}
		else
		{
			result = TRUE;
		}
	}
	return result;
}

char popChar(char* data,int charType)
{
	char result = 0x0;
	int i = 0;
	int strLength = 0;

	strLength = (int)strlen(data);

	do
	{
		//检查字符数组的长度
		//是否有数据需要处理
		if (0 == strLength)
			break;

		//对比当前字符与处理字符线程所需要的类型是否一致
		//如果一致,将其从数组中移除
		//否则,退出
		if (FALSE == checkType(data[0], charType))
			break;


		if (strLength > 1)
		{
			//如果字符数组中的数据长度大于1
			//移除第一个字符,后续字符依次前移一位
			result = data[0];

			//pop char from data
			for (i = 0; i < strLength - 1; i++)
			{
				data[i] = data[i + 1];
			}
			//
			data[i] = 0x0;
		}
		else if (strLength == 1)
		{
			//如果只有一个字符
			//直接输出
			result = data[0];

			data[0] = 0x0;
		}
	} while (FALSE);

	return result;
}

void printChar(int charType)
{
	char result = 0x0;
	//wait for synchronization event
	do
	{
		//通过同步类型的事件来实现线程间的同步操作
		//全局共享的字符资源
		WaitForSingleObject(hSynchronizationEvent, INFINITE);


		//根据字符类型决定释放打印当前第一个字符
		//如果打印,该字符会从字符数组中移除
		result = popChar(dataChar,charType);
		if (result)
		{
			printf("tid:%d,char:%c\n", GetCurrentThreadId(), result);
		}
		else if(0==strlen(dataChar))
		{
			printf("no more data!\n");
			SetEvent(hSynchronizationEvent);
			break;
		}

		//将同步事件对象设置为有信号状态
		//接着处理其他字符数据
		SetEvent(hSynchronizationEvent);


	} while (strlen(dataChar));

}

DWORD ThreadRoutine1(void* param)
{
	DWORD result = WaitForSingleObject(hNotificationEvent, INFINITE);
	printf("wait result:%d\n", result);

	//这个线程打印数字类型的字符
	printChar(char_num);
	
	return 0;
}

DWORD ThreadRoutine2(void* param)
{
	DWORD result = WaitForSingleObject(hNotificationEvent, INFINITE);
	printf("wait result:%d\n", result);

	//这个线程打印字符型的字符
	printChar(char_char);
	
	return 0;
}

DWORD ThreadRoutine3(void* param)
{
	DWORD result=WaitForSingleObject(hNotificationEvent, INFINITE);
	printf("wait result:%d\n", result);

	//打印特殊字符
	printChar(char_spec);
	
	return 0;
}

void testevent()
{
	//创建通知类型的事件对象
	//初始状态为无信号
	hNotificationEvent = CreateEventA(NULL, TRUE, FALSE, "testevent_notification");

	//创建同步类型的事件对象
	//初始状态为无信号
	hSynchronizationEvent = CreateEventA(NULL, FALSE, FALSE, "testevent_synchronization");

	if (
		INVALID_HANDLE_VALUE != hNotificationEvent
		&&INVALID_HANDLE_VALUE!=hSynchronizationEvent
		)
	{
		//设置通知事件对象为有信号状态
		SetEvent(hNotificationEvent);

		CreateThread(NULL, 0, ThreadRoutine1, NULL, 0, NULL);
		CreateThread(NULL, 0, ThreadRoutine2, NULL, 0, NULL);
		CreateThread(NULL, 0, ThreadRoutine3, NULL, 0, NULL);

		//设置同步类型的事件为有信号状态
		SetEvent(hSynchronizationEvent);

	}



	//if (INVALID_HANDLE_VALUE != hEvent)
	//{
	//	CloseHandle(hEvent);
	//}
}

void testsync()
{
	testevent();
}

------------------------------------------------------------------------------------------------------------------------------

---

Event 事件通常会用来控制同步操作,通俗地说就是一个流程中下一步操作会等待上一步操作结束才开始。下面是个简单的例子:

////创建事件对象
#define event_name "shared_event"
int main()
{
	int time=100;
	HANDLE event=NULL;
        event=CreateEvent(NULL,TRUE,FALSE,event_name);//创建初始状态为nonsingaled可手动设置的  事件
	if(event!=INVALID_HANDLE_VALUE)
	{
             while(time--)
	     {
		switch(WaitForSingleObject(event,3000))//设置等待时间为3秒,如果超时或者事件被设置为singaled就返回
		{//注意下面case是WaitForSingleObject()返回值,不是GetLastError()的  返回值 
		case WAIT_OBJECT_0://在3秒内,事件被设置为由信号,可以进行下一步操作
			//to do someting
                        ResetEvent(event);//这里一定要重新将状态为singaled设置为nonsingaled 
			printf("the event is set to singled.\n");
			break;
		case WAIT_TIMEOUT://3秒过了,事件没有被设置成有信号状态
			//to do someting
			printf("the WaitForSingleObject timekout elapsed.\n");
			break;
		default:
			{
			printf("an error:0x%x!\n",GetLastError());
                        goto retu;
			}
	         }
	     }
        }
        else
        {
            printf("CreateEvent last error:0x%x\n",GetLastError());
        }
  retu:
        if(event!=INVALID_HANDLE_VALUE)
           CloseHandle(event);
	return 0;
}


////打开事件对象
event=OpenEvent(GENERIC_READ|GENERIC_WRITE,FALSE,event_name);
if(event==INVALID_HANDLE_VALUE)
{
    //完成某个操作后,设置事件为singaled状态
    SetEvent(event);
    CloseHandle(event);
}

在Qt框架中,`QCoreApplication::postEvent` 是一个非常有用的静态方法,它允许开发者将一个事件对象异步投递给指定的 `QObject` 对象。与 `sendEvent()` 不同,`postEvent()` 是非阻塞式的,这意味着它不会立即处理事件,而是将事件放入接收对象的事件队列中,等待事件循环的下一次迭代来处理。这种方式非常适合在多线程环境中使用,或者当希望避免阻塞当前线程时[^4]。 ### 使用方法 `QCoreApplication::postEvent` 的基本使用格式如下: ```cpp void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority); ``` - `receiver`:事件的接收者,即事件将被投递给哪个 `QObject`。 - `event`:要投递的事件对象,通常需要使用 `new` 创建堆上的事件对象。 - `priority`:事件的优先级,决定事件在队列中的处理顺序,默认为 `Qt::NormalEventPriority`。 需要注意的是,由于 `postEvent()` 是异步的,因此传递给它的事件对象必须是动态分配的,否则可能会导致未定义行为。Qt 会在事件处理完成后自动删除该事件对象[^5]。 ### 示例代码 下面是一个简单的示例,展示如何使用 `postEvent()` 发送一个自定义事件: ```cpp #include <QCoreApplication> #include <QObject> #include <QEvent> #include <QDebug> class MyObject : public QObject { Q_OBJECT public: explicit MyObject(QObject *parent = nullptr) : QObject(parent) {} protected: bool event(QEvent *e) override { if (e->type() == QEvent::User) { qDebug() << "Custom event received!"; return true; } return QObject::event(e); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyObject obj; QEvent *event = new QEvent(QEvent::User); QCoreApplication::postEvent(&obj, event); return a.exec(); } ``` 在这个例子中,我们定义了一个继承自 `QObject` 的类 `MyObject`,并重写了其 `event()` 方法以处理自定义事件。然后,我们在 `main()` 函数中创建了一个 `MyObject` 实例,并使用 `QCoreApplication::postEvent` 将一个类型为 `QEvent::User` 的事件投递给它。由于 `postEvent()` 是异步的,事件将在事件循环的下一个迭代中被处理[^1]。 ### 注意事项 - 当使用 `postEvent()` 时,必须确保事件对象是通过 `new` 创建的,因为 Qt 会在事件处理完成后自动删除该对象。如果使用栈上的对象,可能会导致程序崩溃或行为异常。 - 如果事件接收者所在的线程没有运行事件循环,则事件永远不会被处理。因此,在使用 `postEvent()` 之前,确保接收者的线程已经启动了事件循环。 - 事件的优先级可以影响事件的处理顺序,合理设置优先级有助于提高应用程序的性能和响应速度。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值