libevent学习五

event loop

事件循环

一旦你向event_base注册了一些event,那你接下来会希望Libevent等待事件的发生并且通知你。

接口

#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被触发,它就会把它们标记为"活跃的",并且开始去运行他们。

你可以通过设置flags参数去改变event_base_loop()的行为。如果EVLOOP_ONCE被设置,那循环会发生阻塞,去等一些event变成“活跃的”,然后遍历运行这些活跃的event。如果EVLOOP_NONBLOCK被设置,循环不会阻塞去等待event被触发:它只会检查是否有event准备好去触发,如果有直接调用它们的回调。

通常,如果已经当前没有活跃或还未处理完的event,那么循环就会退出。你可以通过设置EVLOOP_NO_EXIT_ON_EMPTY去覆盖默认的退出。例如,如果你想稍后从其他的线程增加event。如果设置了EVLOOP_NO_EXIT_ON_EMPTY,循环会持续运行直到人为的调用了event_base_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()相当于无flags的event_base_loop()。因此,它会持续运行,直至没再有被注册的event或有人调用event_base_loopbreak()或event_base_loopexit()。


循环终止
如果你想提前终止一个event循环。你可以有两个选择,这两个方法稍有不同。

接口

int event_base_loopexit(struct event_base *base,
                        const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
event_base_loopexit()可以延迟一段时间再停止。如果tv参数为空,event_base马上停止循环,不会延迟。如果当前有激活的event,它将会持续运行,直至这些被激活的event都运行结束。

event_base_loopbreak()可以直接退出循环。不同于event_base_loopexit(base, NULL),它会在结束当前event后直接退出。

注意当没有event循环在运行时,调用event_base_loopexit(base, NULL)和event_base_loopbreak(base)产生的处理是不同的:如果下一个循环(使用了EVLOOP_ONCE)被调用loopexit会使下一个启动的event 循环停止。而loopbreak只对当前正在运行的event循环有影响,如果当前没有循环在运行,则不会有任何作用。

这两个方法均成功返回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);
}
例子:运行一个event循环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()是否正常退出。下面的方法可以判断是loopexit或break被调用:

接口

int event_base_got_exit(struct event_base *base);
int event_base_got_break(struct event_base *base);
如果调用了它们,这两个方法会返回true,否则返回false。它们的值会在下次启动event循环时被重置。


重新检测event

通常,Libevent会检测event,然后运行高优先级的被激活的event,然后再重新检测,这样循环。但有时你可能想终止Libevent后,再继续运行它。通过类比event_base_loopbreak(),你可以通过下面这个方法:

接口

int event_base_loopcontinue(struct event_base *);
如果当前没有运行在event回调,event_base_loopcontinue()将不会产生作用。


获取内部缓存时间

有时你想在event回调里面获得一个粗略时间,你不需要调用gettimeofday()(因为gettimeofday()是系统调用,未避免内核和用户的上下文切换,你应该尽量避免系统调用)。

在一个回调内部,你可以请求Libevent提供它开始运行这轮回调时的时间:

接口

int event_base_gettimeofday_cached(struct event_base *base,
    struct timeval *tv_out);
如果event_base正在执行回调,event_base_gettimeofday_cached()会设置缓存时间到tv_out参数上。否则它会调用evutil_gettimeofday()去提供准确的当前时间。成功返回0,失败返回负数。

注意当Libevent开始运行回调时,时间就会被缓存,它多多少少都会有些不准确。如果你的回调花了很长时间,会导致时间更加不准确。你可以通过以下方法去强制更新缓存时间:

接口

int event_base_update_cache_time(struct event_base *base);
成功返回0,失败返回-1。如果event_base没有运行在循环里,不会产生作用。


输出event_base状态

接口

void event_base_dump_events(struct event_base *base, FILE *f);
为了调试程序(或调试Libevent!)你有时可能想知道一份添加到event_base中的所有事件完整的列表。调用event_base_dump_events()会把这些信息写到f中。

为每个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中的event。否则未定义的情况可能会发生,包括宕机等。

在调用event_base_foreach_event()期间,会持有event_base的锁:这会阻止其他线程去使用event_base,所以确保你的回调不会运行很长时间。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于学习libevent,你可以按照以下步骤进行: 1. 了解libeventlibevent是一个开源的事件通知库,它提供了事件驱动的网络编程接口,可以用于开发高性能的网络服务器和客户端应用。它支持多种I/O模型(包括基于事件的和多线程的),并提供了跨平台的兼容性。 2. 安装libevent:你可以从libevent的官方网站(https://libevent.org/)上下载最新版本的libevent,并按照官方文档中的指南进行安装。根据你使用的操作系统不同,安装步骤可能会有所不同。 3. 学习libevent的基本概念:了解libevent中的一些核心概念,如事件循环(event loop)、事件处理器(event handler)、事件回调函数(event callback)等。理解这些概念对于正确使用libevent非常重要。 4. 掌握libevent的使用方法:学习如何使用libevent来编写网络应用程序。这包括创建事件循环、注册事件、定义事件回调函数等。libevent提供了丰富的API,你可以根据自己的需求选择合适的接口进行开发。 5. 深入研究libevent的高级特性:学习libevent的更高级功能,如定时器、信号处理、缓冲区管理等。这些功能可以帮助你更好地控制和优化你的网络应用。 6. 查阅文档和示例代码:libevent的官方网站提供了详细的文档和示例代码,你可以利用这些资源来加深对libevent的理解。此外,还可以参考一些开源项目中使用libevent的实际案例,以便更好地应用于自己的项目中。 记住,学习任何新的库或工具都需要有耐心和实践。不断尝试和练习,结合实际项目,才能更好地掌握和应用libevent。祝你学习顺利!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值