介绍
事件驱动编程,它基于事件在多线程中以一定的顺序来执行某些操作,事件根据不同的要求使用不同的数据结构存储,如果保持先进先出,可以使用队列,如果要保持优先级,则可以使用堆。
代码
在这里,我们以队列来保存事件,这样可以保证先进来的事件先执行。其中锁是为了保证多线程安全,而条件变量是为了保证当队列中有数据时及时的唤醒其它等待线程,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_;
};
接下来就是事件的定义,首先定义事件基类Event,operate方法定义事件具体的操作。而EmptyEvent、PrintEvent、TestEvent以及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”]