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,所以确保你的回调不会运行很长时间。

时间: 2024-11-02 20:38:35

libevent学习五的相关文章

libevent学习五(Helper functions and types for Libevent)

基础类型   #ifdef WIN32 #define evutil_socket_t intptr_t #else #define evutil_socket_t int #endif ev_ssize_t   时间兼容函数 //前2个参数的计算结果放到第三个参数里面 #define evutil_timeradd(tvp, uvp, vvp) /* ... */ #define evutil_timersub(tvp, uvp, vvp) /* ... */ #define evutil_t

libevent学习之二:Windows7(Win7)下编译libevent

Linux下编译参考源码中的README文件即可,这里主要记录Windows下的编译. 一.准备工作 去官网下载最新的稳定发布版本libevent-2.0.22-stable 官网地址:http://libevent.org/ 二.使用VS2012编译 1.解压libevent到C:\Users\zhang\Desktop\libevent-2.0.22-stable 2.打开“VS2012开发人员命令提示”工具,如下图所示. 3.输入指令开始编译,如下图所示. 有网友说编译之前应该在以下3个文

Beaglebone Back学习五(PWM测试)

PWM测试 参考链接 1 Enable PWM on BeagleBone with Device Tree overlays 2Using PWM on the Beaglebone Black 3 Beaglebone Coding 101: Buttons and PWM 4 Using PWM outputs 5 beaglebone-black-cpp-PWM 6 Enabling PWM Support in the kernel 7 Beaglebone Back学习五(PWM测试

TweenMax动画库学习(五)

目录            TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)            TweenMax动画库学习(四)            TweenMax动画库学习(五)  

NodeJS学习五 之网页显示

我们新建一个Server.js. 然后运行cmd 找到server.js 路径.  node server.js 浏览器中输入http://localhost:8888/ 你会发现,网页上出现了 Hello World NodeJS学习五 之网页显示,布布扣,bubuko.com

Cmdlet开发与学习(五)

Parameter       在声明cmdlet参数的时候,我们使用了Parameter标识符,在这个标识符中,有些参数需要了解到. 强制参数 设置Mandatory=true即可. 对于强制参数,不管是在命令行中绑定,还是通过管道输入,在命令逻辑执行之前,它就必须绑定好.如果强制参数没有参数值的话,PowerShell会弹出对话框,要求用户提供参数值. 位置参数.       Position= n       有时,我们在使用Powershell的时候,发现仅仅是输入参数值,并没有指定具体

Objective C 快速入门学习五

<一>继承和多态 @class Complex 声明类(同C++) 子类函数成员 super 访问父类 同C++类似 1.通过继承 在子类中添加新方法 2.通过继承 在子类中添加新成员 3.通过继承 实现多态(实现比较简单,通过Id通用类型作为父类) 4.重载 5.抽象类abstract作用:创建子类更容易:提供了处理所有派生子类的公共接口:抽象方法制定了标准协议,规范子类必须实现. 6.通用类型id,编译时不会做类型检查,在运行时才会动态绑定具体类型,指出错误. 静态类型在编译阶段就会指出错

libevent学习__学习历程总结

The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. Furthermore, libevent also support callbacks due to signals or regular timeouts. 环境搭建 下载: http

【git学习五】git基础之git分支

1.背景 最早用github的时候,我傻傻的问舍友大神,git里面的branch是干什么的,他用了很直白的解释,我至今还记得."branch就是你可以自己建立一个分支,随便乱搞而不影响整个项目".git分支应该是git最nb的特色吧,分支的建立和合并都十分方便. 大体的原理是这样的(图借用progit),比如说我们有一个master,还有一个分支是testing,git用head指针标记哪个分支正在被执行操作. 2.分支管理 首先来说下我们的现有的master,它是一个readme文件