1 运行循环
有了event_base,并且有一些event注册到其中(关于怎样创建和注册event请参考下一章节),就需要等待这些event֪ͨ你他们发生了什么。
接口
#define EVLOOP_ONCE 0x01 #define EVLOOP_NONBLOCK 0x02 #define EVLOOP_NO_EXIT_ON_EMPTY 0x04 int event_base_loop(struct event_base *base, int flags);
通常event_base_loop()函数运行一个event_base直到其中再无event注册进来。循环运行,然后不断重复判断是否有已经注册的event触发(例如如果读event的文件描述符准备读或者event的超时即将到来)。一旦这些情况发生,就会标记所有的触发event为激活状态,然后开始运行这些事件(比喻读IO)。
设置flags参数一个或多个标记可改变event_base_loop()的行为。设置EVLOOP_ONCE循环将会由event激活,然后运行这些event,直到再无event后返回。设置EVLOOP_ONCE循环不会再等待有event触发,只会检查是否有event做好准备立刻触发,有则运行其回调函数。
通常没有等待的或者已经激活的event循环将退出,可用通过设置EVLOOP_NO_EXIT_ON_EMPTY标志来覆盖默认行为,例如你要从别的线程添加event。如果设置了EVLOOP_NO_EXIT_ON_EMPTY标志,循环将一直运行,直到调用event_loopbreak()函数或调用了event_base_loopexit()或发生了错误。
完成工作后,如果正常退出event_base_loop()返回0,如果后台发生了一些未知错误则返回-1,如果因为没有等待或激活的event退出则返回1。
为了帮助理解,这里有一个与event_base_loop算法相似的总结:
伪代码
while (any events are registered with the loop,or EVLOOP_NO_EXIT_ON_EMPTY was set) { if (EVLOOP_NONBLOCK was set, or any events are already active) If any registered events have triggered, mark them active. else Wait until at least one event has triggered, and mark it active. for (p = 0; p < n_priorities; ++p) { if (any event with priority of p is active) { Run all active events with priority of p. break; /*Do not run any events of a less important priority*/ } } if (EVLOOP_ONCE was set or EVLOOP_NONBLOCK was set) break; }
为了方便可用这样调用:
接口
int event_base_dispatch(struct event_base *base);
event_base_dispatch()等同于没有设置标志的event_base_loop(),所以event_base_dispatch()将一直运行,直到没有已经注册的事件了,或者调用event_base_loopbreak()或者event_base_loopexit()为止。
这些函数定义在<event2/event.h>中,从libevent1.0版就存在了。
2 停止循环
如果想在移除所有已注册的事件之前停止活动的事件循环,可以调用两个稍有不同的函数。
接口
int event_base_loopexit(struct event_base *base, const struct timeval *tv); int event_base_loopbreak(struct event_base *base);
event_base_loopexit()让event_base在给定时间之后停止循环。如果tv参数为NULL,event_base会立即停止循环,没有延时。如果event_base当前正在执行任何激活事件的回调,则回调会继续运行,直到运行完所有激活事件的回调之才退出。
event_base_loopbreak()让event_base立即退出循环。它与event_base_loopexit(base, NULL)的不同在于,如果event_base当前正在执行激活事件的回调,它将在执行完当前正在处理的事件后立即退出。
注:event_base_loopexit(base, NULL)和event_base_loopbreak(base)在事件循环没有运行时的行为不同:前者安排下一次事件循环在下一轮回调完成后立即停止(就好像带EVLOOP_ONCE标志调用一样);后者却仅仅停止当前正在运行的循环,如果事件循环没有运行,则没有任何效果。
这两个函数都在成功时返回0,失败时返回-1。
示例:立刻关闭
#include <event2/event.h> /* Here’s a callback function that calls loopbreak */ void cb(int sock, short what, void *arg) { struct event_base *base = arg; event_base_loopbreak(base); } void main_loop(struct event_base *base, evutil_socket_t watchdog_fd) { struct event *watchdog_event; /* Construct a new event to trigger whenever there are any bytes to read from a watchdog socket. When that happens, we’ll call the cb function, which will make the loop exit immediately without running any other active events at all.*/ watchdog_event = event_new(base, watchdog_fd, EV_READ, cb, base); event_add(watchdog_event, NULL); event_base_dispatch(base); }
示例:执行事件循环10秒后退出
#include <event2/event.h> void run_base_with_ticks(struct event_base *base) { struct timeval ten_sec; ten_sec.tv_sec = 10; ten_sec.tv_usec = 0; /* Now we run the event_base for a series of 10-second intervals, printing "Tick" after each. For a much better way to implement a 10-second timer, see the section below about persistent timer events.*/ while (1) { /* This schedules an exit ten seconds from now.*/ event_base_loopexit(base, &ten_sec); event_base_dispatch(base); puts("Tick"); } }
有时候需要知道event_base_dispatch()或event_base_loop()的调用是正常退出的,还是因为调用event_base_loopexit()或者event_base_break()而退出的。可以调用下述函数来确定是否调用了loopexit或者break函数。
接口
int event_base_got_exit(struct event_base *base); int event_base_got_break(struct event_base *base);
这两个函数分别会在循环时因为调用event_base_loopexit()或者event_base_break()而退出的时候返回true,否则返回false。下次启动事件循环的时候,这些值会被重设。这些函数声明在<event2/event.h>中。event_break_loopexit()函数首次在libevent1.0c版本中实现;event_break_loopbreak()首次在libevent1.4.3版本中实现。
3 检查事件
通常LibEvent检查event,运行所有活动的event中的优先级最高的event,然后再次检查活动的event,如此循环。但是有时候当回调函数运行后就停止LibEvent,然后告诉它再来扫描,相比event_base_loopbreak()函数你可调用event_base_loopcontinue()函数来实现这些功能。
接口
int event_base_loopcontinue(struct event_base *);
如果没有运行回调函数调用event_base_loopcontinue()将不产生任何影响。该函数在LibEvent2.1.2-alpha版本中有介绍。
4 检查内部时间缓存
有时候需要在事件回调中获取当前时间的近似视图,但不想调用gettimeofday()(可能是因为OS将gettimeofday()作为系统调用实现,而你试图避免系统调用的开销)。在回调中,可以请求libevent开始本轮回调时的当前时间视图。
接口
int event_base_gettimeofday_cached(struct event_base *base,struct timeval *tv_out);
如果当前正在执行回调,event_base_gettimeofday_cached()函数设置tv_out参数的值为缓存的时间。否则,函数调用evutil_gettimeofday()获取真正的当前时间。成功时函数返回0,失败时返回负数。
注意,因为libevent在开始执行回调的时候缓存时间值,所以这个值至少是有一点不精确的。如果回调执行很长时间,这个值将非常不精确。要强制刷新缓存,你可以调用:
接口
int event_base_update_cache_time(struct event_base *base);
函数成功返回0,失败返回-1。如果base没有运行其循环,则该函数对这些没有影响。这个函数是libevent2.0.4-alpha新引入的。
5 转存event_base状态
接口
void event_base_dump_events(struct event_base *base, FILE *f);
为帮助调试程序(或者调试libevent),有时候可能需要加入到event_base的事件及其状态的完整列表。调用event_base_dump_events()可以将这个列表输出到指定的文件中。这个列表是人可读的,未来版本的libevent 将会改变其格式。这个函数在libevent2.0.1-alpha版本中引入。
6 每个event_base上运行一个event
接口
typedef int (*event_base_foreach_event_cb)(const struct event_base *, const struct event *, void *); int event_base_foreach_event(struct event_base *base, event_base_foreach_event_cb fn, void *arg);
你可以运行event_base_foreach_event来遍历每个与event_base有关活动或等待event。提供的回调函数将会被每个event被不确定的顺序调用。event_base_foreach_event()的第三个参数将作为第三个参数传递到每个调用回调。
回调函数必须返回0去继续遍历,如果是其他的整数将停止迭代。无论回调函数返回什么值都是由event_base_foreach_function()返回。
回调函数不能编辑接收的任何event,不能增加和删除任何event_base的event,该函数将阻塞别的线程对event_base做任何事,所以要保证回调函数不会耗时过长。
7 废弃的事件回调函数
如上所示,老版本的LibEvent都有一个全局"当前"event_base的概念。这些函数表现得像当前函数,除了它们都没有基本参数。
注意
因为在libEvent2.0版本之前event_base并不支持锁,所以这些函数并不是完全线程安全的。不允许调用_loopbreak()或者_loopexit()去执行别的线程的event循环。
来源:51CTO
作者:守望178
链接:https://blog.csdn.net/w00347190/article/details/101059867