一个 cpp 协程库的前世今生(十九)event
为了防止大家找不到代码,还是先贴一下项目链接:
GitHub - skyfireitdiy/cocpp at cocpp-0.1.0
在软件开发过程中,经常会出现模块依赖复杂,增加模块间的耦合性的场景。这时,回调函数可以解决一部分问题,但是使用回调函数扩展性比较差,通常一个位置的回调函数只能设置一个,当一个位置已经注册了一个回家函数的时候,想要更改他的行为就会比较麻烦。
本文将展示一个 cocpp 中使用的回调函数技巧,cocpp 中将他命名为 event,但是这个传统意义上的事件又是不一样的,他没有事件循环,但是使用起来是类似的,因此将其称为事件。
接口
event 的接口定义在 include/cocpp/comm/co_event.h:
可以看出这是一个模板类。一共只有三个接口:
sub 订阅一个事件,参数为事件处理函数,返回值为订阅的 ID
pub 发布一个事件,参数即事件的参数
unsub 取消订阅一个事件,参数为从 sub 返回的 ID
接下来再看一下成员变量:
cb_list__ 事件处理函数的集合,也就是说同一个事件可以被多个事件处理函数处理(这弥补了普通回调函数扩展性差的缺陷)。
mu_cb_list__ 保护处理函数集合的自旋锁。
current_handler__ 当前的最大 ID,用于为下一个事件处理函数分配 ID
除此之外,为了方便使用,在 include/cocpp/comm/co_event.h 中还定义了一个宏:
这个宏的作用是生成一个 event 实例,并添加一个成员函数获取这个实例。
实现
接下来我们看一下实现,因为是模板类,因此它的实现依然在 include/cocpp/comm/co_event.h 中。
sub
sub 的实现如下:
本质上就是在回调函数列表中添加一个项,并返回对应的 ID。
unsub
unsub 对应的实现如下:
根据传入的 ID 从回调函数的列表(实际上是一个 map)中,删除对应的回调函数。
pub
pub 的实现其实就是调用回调函数列表中的回调函数:
该函数会遍历回调函数列表,然后依次执行的回调函数。因为我们使用的是 map 来存储回调函数,并且我们的 ID 是从小到大递增的,因此这里的回调函数执行顺序其实就是 sub 的顺序(除非 sub 的数量发生了整数溢出)。
使用
接下来我们看一下 cocpp 中如何使用 event:
注册事件
以下代码片段见 include/cocpp/core/co_ctx.h:
此处向 ctx 类中注册了 priority_changed 事件,该事件携带两个 int 类型的参数。
注册事件处理
以下代码片段见 source/cocpp/core/co_manager.cpp:
这里为 ctx 的 priority_changed 事件注册了处理函数,此处为一个 lambda 表达式。
发布事件
以下代码片段见 source/cocpp/core/co_ctx.cpp:
当优先级被修改时,就会调用上面注册的事件处理函数。
如果我们想监控每一个 ctx 优先级的变化情况,我们可以继续 sub priority_changed 这个事件,这就是扩展性的体现。
总结
回调函数在降低耦合的场景下很有用,但是其自身具有一定的局限性,扩展性不好。通过对它进行封装,我们既可以得到低耦合又可以得到扩展性。
版权声明: 本文为 InfoQ 作者【SkyFire】的原创文章。
原文链接:【http://xie.infoq.cn/article/88f790eceb3f9cc52cec0a912】。文章转载请联系作者。
评论