相关文章推荐
留胡子的电影票  ·  org.springframework.jd ...·  2 年前    · 
奔放的水煮肉  ·  android - Difference ...·  2 年前    · 

在你能使用任何感兴趣的LibEvent函数之前,你需要 分配一个或多个event_base结构体 。每个event_base结构体 拥有一系列的event,并且可以通过轮询判断哪个event是激活的
如果一个event_base设置为使用锁定,在多个线程中 访问它是安全的 ,然而 只能在一个线程中去处理 其事件循环 。如果你想在多线程中轮询IO,那么你需要 为每个线程分配一个event_base

未来版本将会支持event_base多线程运行

每个event_base都有一个 方法或后台 用来决定 哪个event已经准备好了 。已经验证的可行方法是:

  • select;
  • poll;
  • epoll;
  • kqueue;
  • devpoll;
  • evport;
  • win32;

用户可以通过一些 环境变量来禁用某些后台 ,如果想要kqueue后端,设置变量EVENT_NOKQUEUE,诸如此类。如果你想要从程序内部关闭后台,请查看下面的event_config_avoid_method()方法。

1 创建默认的event_base

event_base()函数分配和返回了一个 默认参数 的event_base,它检验环境变量然后分配了一个指向新的event_base的指针,如果错误则返回NULL。
选择各种方法时,函数会选择其中操作系统支持的最快方法。

struct event_base *event_base_new(void);

就大多数程序而言,这个函数就已经足够了。
event_base_new()函数声明在<event2/event.h>中,首次出现在 libevent1.4.3 版。

2 创建复杂的event_base

如果你想要更多控制你获取到的那种event_base,就需要event_config。event_config是一个不透明结构体,它保存了你设置给event_base的配置信息。当想创建一个event_base时,需要将event_config传入event_base_new_with_config()。

struct event_config *event_config_new(void);
struct event_base *event_base_new_with_config(const struct event_config *cfg); 
void event_config_free(struct event_config *cfg);

要用这些函数来创建一个新的event_base,你需要调用event_config_new()函数来分配一个新的event_config,然后调用别的函数,用event_config 告诉它你的需求。最后,调用event_base_new_with_config()函数来获取一个新的event_base,当这些完成后就可以使用event_config_free()函数来释放event_config。

int event_config_avoid_method(struct event_config *cfg, const char *method);
enum event_method_feature 
    EV_FEATURE_ET = 0x01, 
    EV_FEATURE_O1 = 0x02, 
    EV_FEATURE_FDS = 0x04, 
int event_config_require_features(struct event_config *cfg, enum event_method_feature feature); 
enum event_base_config_flag 
    EVENT_BASE_FLAG_NOLOCK = 0x01, 
    EVENT_BASE_FLAG_IGNORE_ENV = 0x02, 
    EVENT_BASE_FLAG_STARTUP_IOCP = 0x04, 
    EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08, 
    EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, 
    EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 
int event_config_set_flag(struct event_config *cfg,enum event_base_config_flag flag);

调用event_config_avoid_method()可以通过名字让libevent避免使用特定的可用后端。调event_config_require_feature()让libevent不使用不能提供所有指定特征的后端。调用event_config_set_flag()让libevent在创建event_base时设置一个或者多个将在下面介绍的运行时标志。

event_config_require_features()可识别的特征值有:

  • EV_FEATURE_ET:要求支持边沿触发的后端;
  • EV_FEATURE_O1:要求添加、删除单个事件,或者确定哪个事件激活的操作是 O(1)复杂度的后端;
  • EV_FEATURE_FDS:要求支持任意文件描述符,而不仅仅是套接字的后端;

event_config_set_flag()可识别的选项值有:

  • EVENT_BASE_FLAG_NOLOCK:不要为event_base分配锁。设置这个选项可以为event_base节省一点用于锁定和解锁的时间,但是让在多个线程中访问event_base成为不安全的;
  • EVENT_BASE_FLAG_IGNORE_ENV:选择使用的后端时,不要检测EVENT_*环境变量。使用这个标志需要三思:这会让用户更难调试你的程序与libevent的交互;
  • EVENT_BASE_FLAG_STARTUP_IOCP:仅用于Windows,让libevent在启动时就启用任何必需的IOCP分发逻辑,而不是按需启用;
  • EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告诉libevent,如果决定使用epoll后端,可以安全地使用更快的基于changelist的后端。epoll-changelist后端可以在后端的分发函数调用之间,同样的fd多次修改其状态的情况下,避免不必要的系统调用。但是如果传递任何使用dup()或者其变体克隆的fd给libevent,epoll-changelist后端会触发一个内核bug,导致不正确的结果。在不使用epoll后端的情况下,这个标志是没有效果的。也可以通过设置EVENT_EPOLL_USE_CHANGELIST环境变量来打开epoll-changelist选项;
  • EVENT_BASE_FLAG_NO_CACHE_TIME:不是在事件循环每次准备执行超时回调时检测当前时间,而是在每次超时回调后进行检测。注 意:这会消耗更 多的CPU时间;

上述操作event_config的函数都在成功时返回 0,失败时返回-1。

注意
设置event_config,请求OS不能提供的后端是很容易的。比如说,对于libevent2.0.1-alpha,在 Windows中是没有O(1)后端的;在Linux中也没有同时提供EV_FEATURE_FDS和EV_FEATURE_O1特征的后端。如果创建了libevent不能满足的配置。event_base_new_with_config()会返回 NULL。

int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)

虽然这个函数将来可能会在别的平台上起作用,但就目前来讲它只限于使用Windows的完成端口(IOCP)时使用。调用这个函数来通知event_config创建的event_base要在多线程下充分利用好传入的cpu数量。这只是一个暗示,event_base也可能调高cpu数量或降低cpu数量。

int event_config_set_max_dispatch_interval(struct event_config *cfg, const struct timeval *max_interval, int max_callbacks,int min_priority);

该函数能够限制优先级反转,通过在检查更高权限之前限制优先级的事件回调函数调用。如果变量max_interval不为空,则事件循环会去检验函数返回后的时间,如果max_interval时间过去了,那么就会搜寻那些更高优先级的事件。如果max_callbacks参数非负,事件循环也会在 max_callbacks个回调函数返回后检查更多事件。这种规则适用于任何事件的优先级高低算法。

示例:设置边缘触发

struct event_config *cfg; 
struct event_base *base; 
int i; 
/* My program wants to use edge-triggered events if at all possible. So I’ll try to get a base twice: Once insisting on edge-triggered IO, and once not.*/ 
for (i = 0; i < 2; ++i) 
    cfg = event_config_new(); 
    /* I don’t like select.*/ 
    event_config_avoid_method(cfg, "select"); 
    if (i == 0) event_config_require_features(cfg, EV_FEATURE_ET); 
    base = event_base_new_with_config(cfg); 
    event_config_free(cfg); 
    if (base) break; 
    /* If we get here, event_base_new_with_config() returned NULL. If this is the first 
       time around the loop, we’ll try again without setting EV_FEATURE_ET. If this is the 
       second time around the loop, we’ll give up.*/ 

示例:避免优先级反转

struct event_config *cfg; 
struct event_base *base; 
cfg = event_config_new(); 
if (!cfg) 
/* Handle error */; 
/* I’m going to have events running at two priorities. I expect that some of my priority-1 events are going to have pretty slow callbacks,so I don’t want more than 100 msec to elapse (or 5 callbacks) before checking for priority-0 events.*/ 
struct timeval msec_100 = { 0, 100*1000 }; 
event_config_set_max_dispatch_interval(cfg, &msec_100, 5, 1); 
base = event_base_new_with_config(cfg); 
if (!base) /* Handle error */; 
event_base_priority_init(base, 2);

这些函数和类型定义在<event2/event.h>中。
EVENT_BASE_FLAG_IGNORE_ENV标志首次出现是在LibEvent2.0.2-alpha版本中,EVENT_BASE_FLAG_PRECISE_TIMER标志首次出现是在LibEvent2.1.2-alpha版本中。event_config_set_num_cpus_hint()函数最新版本在LibEvent2.0.7rc,event_config_set_max_dispatch_interval()最新在LibEvent2.1.1-alpha版本,其余是在LibEvent2.0.1-alpha中首次出现。

3 检查event_base的后台方法

某些时候需要查看event_base实际支持哪些特征,运行时支持哪些方法。

const char **event_get_supported_methods(void); 

event_get_supported_methods返回了指向LibEvent本版本支持的函数的名称数组的指针,数组最后一个元素为NULL。

struct event_base *base; 
enum event_method_feature f; 
base = event_base_new(); 
if (!base) 
    puts("Couldn’t get an event_base!"); 
    printf("Using Libevent with backend method %s.",event_base_get_method(base)); 
    f = event_base_get_features(base); 
    if ((f & EV_FEATURE_ET)) 
        printf(" Edge-triggered events are supported."); 
    if ((f & EV_FEATURE_O1)) 
        printf(" O(1) event notification is supported.");
    if ((f & EV_FEATURE_FDS)) 
        printf(" All FD types are supported."); 
    puts(""); 

这些函数定义在<event2/event.h>中,event_base_get_method()函数首次调用可行是在LibEvent1.4.3,别的函数首次出现是在LibEvent2.0.1-alpha中。

4 释放event_base

当用完一个event_base之后,可以使用event_base_free()函数来释放它。

void event_base_free(struct event_base *base);

注意,该函数不会释放释放event_base关联的任何event,也不会关闭任何sockets,更不会释放它们的任何指针。
event_base_free()函数定义在<event2/event.h>中,首先实现在LibEvent1.2。

5 设置event_base中event优先级

LibEvent支持对event设置多个优先级。默认情况下,event_base只支持一个优先级。你可以通过调用event_base_priority_init()来设置event 的优先级数量。

int event_base_priority_init(struct event_base *base, int n_priorities);

函数成功返回0,失败返回-1。参数base是需要编辑的event_base指针,n_priorities是需要支持的优先级的数量,该值至少为1。一个新的event的优先级从0(最重要)到n_priorities-1(最不重要)。
常量EVENT_MAX_PRIORITIES设置了优先级值的上限,当n_priorities高于此上限时,调用该函数会报错。

注意
你必须在所有event开始活动之前调用该函数,最好的做法是创建完event_base之后立刻调用。 

为了找到由当前的event_base支持的优先级数量,可用调用event_base_get_npriorities()。

int event_base_get_npriorities(struct event_base *base);

函数返回值等价于event_base配置的优先级数量。如果event_base_get_npriorities()函数返回的是3,那么优先级是0,1,2。

示例请参考下面的event_priority_set的文档

默认情况下,关联到该base的event将会被设置为优先级等于n_priorities/2。
event_base_priority_init()函数定义在<event2/event.h>中,从LibEvent1.0版本开始可用。event_base_get_npriorities()函数首次出现在LibEvent2.1.1-alpha版本。

6 Fork()之后重新初始化一个event_base

不是所有的后台event_base都会在fork()调用完成后坚持清理。如果你想要你的程序调用fork()或者其他系统API来创建一个新的进程,并且fork完成后又想继续使用该event_base,你就需要重新初始化它。

int event_reinit(struct event_base *base); 

函数成功返回0,失败返回-1。

struct event_base *base = event_base_new(); 
/*... add some events to the event_base ...*/ 
if (fork()) 
    /* In parent */ 
    continue_running_parent(base); 
    /*...*/ 
    /* In child */ 
    event_reinit(base); 
    continue_running_child(base); 
    /*...*/ 

event_reinit()函数定义在<event2/event.h>中,首次可用是在LibEvent1.4.3-alpha版本。

7 废弃的event_base函数

老版本的LibEvent相当依赖"当前"event_base的想法,"当前"event_base是一个多线程共享的全局设置。如果忘记指定了哪个event_base,就用当前这个。由于event_base不是线程安全的,所以这样很容易出错。
而取代event_base_new()的方法是:

struct event_base *event_init(void);

这个函数工作机制与event_base_new()函数相同,设置当前的base到分配的base,除此之外再无别的方法可用设置当前的base。
关于这一块,有一些event_base函数有多种版本。这些函数与当前函数都较像,除了不传base参数。

在你能使用任何感兴趣的LibEvent函数之前,你需要分配一个或多个event_base结构体。每个event_base结构体拥有一系列的event,并且可以通过轮询判断哪个event是激活的。如果一个event_base设置为使用锁定,在多个线程中访问它是安全的,然而只能在一个线程中去处理其事件循环。如果你想在多线程中轮询IO,那么你需要为每个线程分配一个event_base。注意未来...
  event_baselibevent的事务处理框架,负责事件注册、删除等,属于Reactor模式中的Reactor。 event_base结构体   event_base结构体定义于<event_internal.h>中: 1 struct event_base { 2 /** Function pointers and other data...
struct event_config { // 这个队列中存放的是config中需要避免的IO多路复用模型 TAILQ_HEAD(event_configq, event_config_entry) entries; // CPU的个数,仅仅在win下的iocp设置了CPU个数后会智能的调整 // event_config_set_num_cpus_hint 可以设置 int n_c...