在你能使用任何感兴趣的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_base是libevent的事务处理框架,负责事件注册、删除等,属于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...