事件驱动编程

介绍

    事件驱动编程,它基于事件在多线程中以一定的顺序来执行某些操作,事件根据不同的要求使用不同的数据结构存储,如果保持先进先出,可以使用队列,如果要保持优先级,则可以使用堆。

代码

    在这里,我们以队列来保存事件,这样可以保证先进来的事件先执行。其中锁是为了保证多线程安全,而条件变量是为了保证当队列中有数据时及时的唤醒其它等待线程,push操作是把事件添加到队列的末尾,而pop方法则是把首位的事件拿出来并且在队列中删除。

#pragma once

#include <queue>
#include <mutex>
#include <condition_variable>

template<class T>
class BlockQueue
{
public:
	BlockQueue()
		: queue_()
		, mutex_()
		, cond_(){

	}

	~BlockQueue() {

	}

	void push(const T& t) {
		{
			std::unique_lock<std::mutex> lock(mutex_);
			queue_.push(t);
		}
		cond_.notify_all();
	}

	T pop() {
		std::unique_lock<std::mutex> lock(mutex_);
		while (queue_.empty()) {
			cond_.wait(lock);
		}
		T front(queue_.front());
		queue_.pop();
		return front;
	}
private:
	std::queue<T> queue_;
	std::mutex mutex_;
	std::condition_variable cond_;
};

    同时,我们特化一下队列模板类,因为大多数时候队列存放的都是指针数据。

template<class T>
class BlockQueue<T*>
{
public:
	BlockQueue()
		: queue_()
		, mutex_()
		, cond_() {

	}

	~BlockQueue() {

	}

	void push(T * t) {
		{
			std::unique_lock<std::mutex> lock(mutex_);
			queue_.push(t);
		}
		cond_.notify_all();
	}

	T* pop() {
		std::unique_lock<std::mutex> lock(mutex_);
		while (queue_.empty()) {
			cond_.wait(lock);
		}
		T* front(queue_.front());
		queue_.pop();
		return front;
	}
private:
	std::queue<T*> queue_;
	std::mutex mutex_;
	std::condition_variable cond_;
};

    接下来就是事件的定义,首先定义事件基类Eventoperate方法定义事件具体的操作。而EmptyEventPrintEventTestEvent以及QuitEvent都是具体的事件类,他们拥有不同的操作。

class Event
{
public:
	virtual ~Event() {

	}
	virtual int operate(int op, int data) = 0;
private:
};

class EmptyEvent : public Event
{
public:
	const static int EVENT_VALUE = 0;
	virtual int operate(int op, int data) {
		//std::cout << "error event value." << std::endl;
		std::cout << "EmptyEvent op = " << op << ", data = " << data << std::endl;
		return 0;
	}
private:
};

class PrintEvent : public Event
{
public:
	const static int EVENT_VALUE = 1;
	virtual int operate(int op, int data) {
		std::cout << "PrintEvent op = " << op << ", data = " << data << std::endl;
		return 0;
	}
private:
};

class TestEvent : public Event
{
public:
	const static int EVENT_VALUE = 2;
	virtual int operate(int op, int data) {
		std::cout << "TestEvent op = " << op << ", data = " << data << std::endl;
		return 0;
	}
private:
};

class QuitEvent : public Event
{
public:
	const static int EVENT_VALUE = 3;
	virtual int operate(int op, int data) {
		std::cout << "QuitEvent op = " << op << ", data = " << data << std::endl;
		exit(0);
		return 0;
	}
private:
};

    然后,我们根据不同的事件类型,我们使用简单工厂模式来创建不同的事件。由于工厂类只需要一个,故在这里使用单件模式来定义全局唯一。在这里createEvent方法会根据不同的事件类型,返回不同的事件对象指针。

typedef struct EventData_
{
	int ev;
	int op;
	int data;
}EventData;

class EventFactory
{
public:
	static EventFactory *instance() {
		static EventFactory *g_ef = nullptr;
		/// not thread safe
		if (nullptr == g_ef) {
			g_ef = new EventFactory;
		}
		return g_ef;
	}

	Event* createEvent(int ev) {
		switch (ev) {
		case PrintEvent::EVENT_VALUE:
			return new PrintEvent;
		case TestEvent::EVENT_VALUE:
			return new TestEvent;
		case QuitEvent::EVENT_VALUE:
			return new QuitEvent;
		default:
			return new EmptyEvent;
		}
	}
private:
	EventFactory() {

	}
};

    最后,我们需要定义一个派遣类,来对事件进行入栈和出栈操作。在这里我们对事件进行管理,当用户需要事件时,可以用newED创建一个事件,当操作完成时可以使用freeED删除它。assign完成事件的入栈,get完成事件的出栈以及dispatch完成相应事件的操作,并且返回操作结果。而且在这里也对Dispatch类使用了单件模式,因为全局也只需要使用一个实例。

#pragma once
#include "blockQueue.h"
#include "event.h"

class Dispatch
{
public:
	static Dispatch& instance() {
		static Dispatch g_dis;
		return g_dis;
	}
	~Dispatch() {
		
	}

	void assign(EventData *e) {
		bq_.push(e);
	}

	EventData *get() {
		return bq_.pop();
	}

	int dispatch(EventData *ed) {
		Event *e = EventFactory::instance()->createEvent(ed->ev);
		int ret = e->operate(ed->op, ed->data);
		delete e;
		return ret;
	}

	EventData *newED(){
		return new EventData;
	}

	void freeED(EventData *ed) {
		delete ed;
	}
private:
	Dispatch() : bq_() {

	}
private:
	BlockQueue<EventData*> bq_;
};

    在使用当中,只需要去检测事件队列当中是否有事件,如果有事件,则出栈,并对它做出相应的事件,然后删除相应的数据。如果没有事件,则它进入睡眠,只有当事件入栈时才会被唤醒。

#include "Dispatch.h"
#include <thread>
#include <windows.h>

void randomEvent()
{
	srand((unsigned)time(NULL));
	Dispatch& dis = Dispatch::instance();
	do
	{
		EventData *ed = dis.newED();
		ed->ev = rand() % 5;
		ed->op = rand() % 10;
		ed->data = rand() % 9999;
		dis.assign(ed);
		Sleep(1000);
	} while (true);
}

int main()
{
	Dispatch& dis = Dispatch::instance();
	std::thread t(randomEvent);
	
	EventData *ed = nullptr;
	while (ed = dis.get()) { 
		dis.dispatch(ed);
		dis.freeED(ed);
	}

    return 0;
}

总结

    事件驱动编程的好处就是,可以按着你指定的顺序去执行相应的事件操作。而且在等待事件的过程也不会占用cpu,因为使用的事件会在没有事件时进入睡眠状态而让出cpu。
[download id=”1159″ template=”button”]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Z小偉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值