设计心得——事件机制的实现

一、事件和事件机制

事件(Event)和事件机制(事件驱动机制)本身都容易理解,虽然在不同的平台和不同的框架上可能有略微的不同,但事件基本都是一种行为的抽象表示,它可能包括动作也可能是状态也可能是其它的一些触发等。而事件机制作为一种编程范式,本身就是通过程序在运行过程中由事件控制运行逻辑的一种机制。它可以触发事件也可以响应事件,从而达到开发者的目的。
一般来说,实际应用的事件往往都是基于异步实现、被动触发的。
如果不考虑硬件底层的处理,一般来说,异步都是基于线(进)程而实现,然后再以回调的形式进行处理。通常情况下,一套事件机制主要有以下几个部分:
1、事件对象
这个其实是指对事件本身抽象出来的对象,用来对事件进行封装,用来表示数据、消息或动作等。不同的事件框架中,可能封装的大不相同,需要根据实际情况来进行甄别
2、事件源(触发器)
这个很好理解,就比如在GUI上的按键之类的事件源,还有常见的定时器的到期执行等等
3、事件监听(接收)器
就是在收到事件后,进行事件响应的部分,它一般主要是用来做回调函数的调用,当然也可能做一些数据的处理等
4、事件传递
这一部分相当复杂,可能是一个条件变量,也可能是一个消息,或者是SOCKET,甚至MQ等都可以
5、事件队列
事件队列不是必须的,一般只有功能相对复杂的事件机制才会有
6、事件处理器
这块算是一个事件的调度功能,一个事件机制可能会有一大批不同的事件出来,它需要不断的循环处理事件,比如从事件队列中获取可用事件
7、回调函数
就是经常用到的回调函数,没有什么可以展开的

二、事件机制的特点

事件机制作为一种异步机制,主要的优点包括:
1、效率高,由于不必使用轮询来处理相关操作,大大提高了资源的利用率
2、异步响应,不会影响其它用户或系统的工作
3、解耦,由于事件驱动可以分离依赖的各方,从而解耦大部分的逻辑
4、扩展性高,只要注册新的事件就可以实现设计逻辑
5、灵活的适应性,一个好的事件机制可以为更多的平台所使用
但在前面学习异步编程和回调函数时,就知道了它们的缺点,事件同样也包含:
1、编程的复杂度明显提高,这是异步带来的典型的负面影响
2、调试困难,异步和回调机制导致数据和行为的非同步性
3、维护困难,代码的复杂度增强代表了对维护者的要求的提高

三、事件机制的应用

在开发中使用事件机制,一般需要以下几个步骤:
1、定义事件对象:需要根据事件机制的框架定义相关的事件对象
2、注册事件:将相关的事件对象,特别是事件行为对象通过接口进行事件的注册
3、触发事件:在应用层上通过各种行为触发事件,如点击按钮等
4、事件进入事件队列:相关触发的事件进入事件队列
5、事件处理:通过循环不断的从队列中获取可用事件(非必须)
6、事件动作结果:调用回调函数处理对事件结果的逻辑处理
事件机制的应用非常广泛,无论是从PC还是到服务器还是移动终端,事件机制几乎都是绕不开的话题,一般最常见的应用包括以下几个场景:
1、GUI界面编程
对按键、触屏等的响应,基本都是以事件机制为主的。
2、Web系统
这个一般用来处理网络行为的各种情况,比如常见的网页加载,加载完成等等
3、游戏开发
这个就更容易明白了,比如子弹接触到物品就产生杀伤事件等等
4、IO编程
这个看上去好理解,其实范围很大,不仅限于网络、硬盘、DMA等等常见的IO,也包括一些传感器的相关的接口、协议等等触发的事件
5、系统和框架
这个其实就是一些底层的实现了,可能开发者做的比较少,但应用比较多,比如觉的Node.js,Select等等
上面的几个应用场景基本覆盖了常见事件机制的应用范围。

四、实现

事件机制的实现有很多种方法,常见的有以下几种:
1、进程内
进程内一般是通过回调函数实现,如果想实现异步还会增加异步的相关操作,最典型的就是定时器。常见的定时器的实现中,就是线程+回调,这也和上面分析一致。至于有些系统或者框架提供了定时器,如果有机会看其内部实现,基本也都是类似的方法。当然,可能有别的方法也未可知。

2、进程外
进程外使用的事件循环系统,一般需要解耦进程间的通信,常见的比如Windows平台上的消息机制、Linux平台上的信号机制等。在分布式平台上的应用可能还需要MQ或一些中间件等。比如前面的RPC其实就是要注册一个回调函数到服务上,然后通过客户端进行回调。它的底层实现机制很多,但一般最终都会落到Socket服务上。
也就是说,进程外的事件机制,其实就是通过某种技术实现了分布式不同节点的透明的回调机制。
这里最常见的就是Web开发中的各种回调,Ajax就是一个典型的例子,不过现在可能Web开发者一般不使用它了,有更好的框架和方式来实现相同的目的。

五、实例

这里给出一个最初级的例子和一个相对高级一些的例子,希望大家能从这个例子的简单的变化,能明白事件封装的方向。
最基础例子:

#include <iostream>
#include <functional>
typedef void(*CB)(const std::string &);

void triggerEvent(CB cb, const std::string &msg) {
    std::cout << "trigger event!" << std::endl;
    cb(msg);
}

void onEvent(const std::string &msg ) {
    std::cout << "msg content:"<<msg << std::endl;
}

int main() {
   const std::string msg = "Hello, World!";
   triggerEvent(onEvent,msg);
   return 0;
}
 

这是一个最基础的例子,如果想在多线程中使用就需要处理一些数据的同步或其它操作的安全行为。下面再看一个在些基础上进行封闭的例子,并使用了一些新的C++标准。
初步封装的例程:

#include <iostream>
#include <functional>
#include <vector>

enum class EType :  int{
    kEventAll=1,
    kEventSlot,
    kEventData,
};
template <typename... Args>
class Event {
public:
    using Callback = std::function<void(Args...)>;

    void addNotify(Callback cb) {
        eventsHandle_.emplace_back(std::move(cb));
    }

    void notify(Args... args) {
        for (const auto& handle : eventsHandle_) {
            std::cout<<"event type:"<<static_cast<int>(this->type_)<<std::endl;
            handle(args...);
        }
    }
    void setType(EType et){this->type_ = et;}
    EType gettType()const{return this->type_ ;}
private:
    std::vector<Callback> eventsHandle_;
    EType type_= EType::kEventData;
};

int main() {
    Event<std::string> event;
    event.setType(EType::kEventAll);
    event.addNotify([](std::string&& msg) {
        std::cout << "first notify msg: " << msg << std::endl;
    });
    auto h = [](const std::string& msg) {
        std::cout << "second notify msg: " << msg << std::endl;
    };
    event.addNotify(h);
    event.notify("event is trigger!");
    return 0;
}

上面的例子比前一个要复杂一些,但仍然可以进一步处理更复杂的事件机制,比如在此基础上处理多种类型的事件,就可以再次封装,在循环中处理即可。
如果想了解更深入的事件机制,大家可以去内核中看看Select等的实现,或者去看一看相关框架中对分布式中事件的处理机制,就会更好的理解事件的底层运行机制。很多技术都是从最初的一个原理的基础例子衍生并不断的强大,如果直接去学习后面的版本,可能会走不少的弯路。可一旦明白了其最初的样子,再逐步的展开学习就会发现有柳暗花明、山重水复的感觉。
既不能迷信框架的优秀,又不能为掌握了原理而沾沾自喜!

六、总结

本文主要是给不了解或者想学习事件机制的开发者一个入门的方法,重点在于提供一个初窥门径的视点。在明白了事件机制的原理后,可以根据自己的技术水平的增长和认知思想的不断提高,设计出一个相对更完善更合理的事件系统。万事开头难,临门第一脚。
以后有机会在此基础上将一个相对事用的事件系统给大家剖析一下,那么就更容易在事件系统上总结提高。愿与诸君共勉!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值