LibEvent中文帮助文档-第7章【事件循环】

时间:2022-06-20 20:30:04


LiEvent中文帮助文档--第7章【事件循环】


   返回主目录


Libevent


快速可移植非阻塞式网络编程

 

 

修订历史

版本

日期

作者

备注

V1.0

2016-11-15

周勇

Libevent编程中文帮助文档

 

文档是2009-2012年由Nick-Mathewson基于Attribution-Noncommercial-Share Alike许可协议3.0创建,未来版本将会使用约束性更低的许可来创建.

此外,本文档的源代码示例也是基于BSD"3条款""修改"条款.详情请参考BSD文件全部条款.本文档最新下载地址:

英文:http://libevent.org/

中文:http://blog.csdn.net/zhouyongku/article/details/53431750

请下载并运行"gitclonegit://github.com/nmathewson/libevent- book.git"获取本文档描述的最新版本源码.



<<上一章>>

7.事件循环

7.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为激活状态,然后开始运行这些事件.

 

设置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>, libevent 1.0版就存在了.

 

7.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()函数首次在 libevent 1.0c 版本中实现;event_break_loopbreak()首次在 libevent 1.4.3版本中实现.

 

7.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版本中you介绍.

 

7.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没有运行其循环,则该函数对这些没有影响.

这个函数是 libevent 2.0.4-alpha新引入的.

 

7.5转存Event_base状态

 

接口

void event_base_dump_events(struct event_base *base, FILE *f);   

为帮助调试程序(或者调试 libevent),有时候可能需要加入到 event_base的事件及其状态的完整列表.调用 event_base_dump_events()可以将这个列表输出到指定的文件中.这个列表是人可读的,未来版本的 libevent将会改变其格式.这个函数在 libevent 2.0.1-alpha 版本中引入.

 

7.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_baseevent,该函数将阻塞别的线程对event_base做任何事,所以要保证回调函数不会耗时过长.

 

7.7废弃的事件回调函数

如上所示,老版本的LibEvent都有一个全局"当前"event_base的概念.这些函数表现得像当前函数,除了它们都没有基本参数.

 

当前函数

废弃的current_base版本

event_base_dispatch()

event_dispatch()

event_base_loop()

event_loop()

event_base_loopexit()

event_loopexit()

event_base_loopbreak()

event_loopbreak()

 

____________________________________________________________________________________________________
注意

因为在libEvent2.0版本之前event_base并不支持锁,所以这些函数并不是完全线程安全的.不允许调用_loopbreak()或者_loopexit()去执行别的线程的event循环.

____________________________________________________________________________________________________

 



<<下一章>>