http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html
创建一个event_base
在使用任何libevent函数之前,需要先创建一个或是多个event_base
。每一个event_base
都包含一个事件集合,它可以准确的通知哪一个事件到达了。
如果event_base
使用了锁,那么在多线程下调用就是安全的。它的事件循环只能在一个线程中使用,如果想在多个线程中遍历IO,就需要为每一个线程创建一个event_base
。
将来可能会提供在多个线程下使用的event_bases
。
每个event_base
都有一个解决方案,后端决定使用哪一个,现有的方案有:
- select
- poll
- epoll
- kqueue
- devpoll
- evport
- win32
可以通过环境变量禁用其中的功能,如果想关闭kqueue
,可以设置EVENT_NOKQUEUE
环境变量。如果想从程序中禁用某个方案,可以调用event_config_avoid_method()
设置默认event_base
event_base_new()
申请并返回一个新的event base。如果出错返回NULL
。
接口
struct event_base *event_base_new(void);
设置一个复杂的event_base
如果想自定义一些属性,需要调用event_config
。event_config
是一个不透明的结构体,保存着event_base
的首选项信息。使用event_base_new_with_config()
创建event_base
,创建的时候可以把event_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
,创建完成后可以把这个配置结构体删除了。
接口
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()
可以禁用一些功能。event_config_require_feature()
设置我们使用的功能。event_config_set_flag()
设置多个运行时的标识
event_config_require_features()
可以使用的参数有:
EV_FEATURE_ET
- 使用边界触发
EV_FEATURE_O1
- 添加、删除或是激活一个event是O(1)的操作
EV_FEATURE_FDS
- 可以使用任意的文件描述符,而不仅仅是socket
对于event_config_set_flag()
可以使用的参数有:
EVENT_BASE_FLAG_NOLOCK
- 对于
event_base
的操作,不实用锁,这样可以节省一点点时间,但是在多线程下工作会有问题
EVENT_BASE_FLAG_IGNORE_ENV
- 禁止使用
EVENT_*
所有的环境变量。谨慎使用这个标识,如果出现问题,很难调试。
EVENT_BASE_FLAG_STARTUP_IOCP
- 启动时就使用IOCP,这个仅仅在Windows下有效。
EVENT_BASE_FLAG_NO_CACHE_TIME
- 把默认的在每次事件循环超时的时候检查当前时间,替换成不是整个循环,而是每个超时回调之后都检查当前时间,这样会大大增加CPU的消耗,带来的是更精确的时钟。慎重考虑。
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST
- 这个很明显是针对epoll的,就是如果使用epoll,那么就可以使用changelist。因为changelist可以在同一个fd多次被修改的情况下避免不必要的系统调用;同样,如果如果你传入了通过
dup()
克隆或是它自己的变体的fd给libevent,那么也会导致内核错误。如果不是epoll模型,这个标识是没有作用的。同样可以通过EVENT_EPOLL_USE_CHANGELIST
环境变量打开changelist。
EVENT_BASE_FLAG_PRECISE_TIMER
- libevent在不同平台下都会使用该系统提供的更快的时钟机制,但是如果该系统上有稍微慢一点的但是更精确的时钟机制,可以通过这个参数设置使用。但是如果系统上没有这种慢一点但是更精确的时钟机制,这个标识不会有任何作用。
上面的这些针对event_config
的函数调用返回值都是:0-成功 -1-失败
注意
我们可以很容易的设置event_config
,不管底层系统是否支持。比如在Libevent 2.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下有效,将来可能在其他平台上也可用。调用这个函数就是设置event_config
使得event_base
在多线程下可以有更好的性能。需要注意的是,最终event_base
使用的CPU个数可能比我们设置的多一些,也可能少一些。
接口
int event_config_set_max_dispatch_interval(struct event_config *cfg,
const struct timeval *max_interval, int max_callbacks,
int min_priority);
在libevent中有事件优先级,这个函数的作用就是打破事件优先级,让libevent在高优先级的事件处理之前可以处理多少个低优先级的事件,因为如果高优先级的事件一直很多,处理不完,就会导致低优先级的事件永远无法处理。如果max_interval
传入的不是NULL
,事件循环会在每次回调后检查时钟,当max_interval
超时后就重新搜索高优先级的事件。如果max_callbacks
是非负的,事件循环还会在调用完所有max_callbacks
回调后再调用更多其他事件。这个规则适用于任何min_priority
或更高等级的事件。
示例:使用边界触发
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
cfg = event_config_new();
/* I don't like select. */
//屏蔽使用select方法
event_config_avoid_method(cfg, "select");
if (i == 0)
//使用ET边界触发
event_config_require_features(cfg, EV_FEATURE_ET);
//用上面的cfg创建一个event base
base = event_base_new_with_config(cfg);
//释放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 */;
/* 这里有两个优先级的event在跑。由于优先级1(也就是优先级高)的事件运行的慢,
所以希望优先级0(也就是优先级低)更多的调用一些。
这里设置的就是最多100毫秒或是回调5次之后,肯定调用一次优先级0的事件 */
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);
检测event_base
底层有效的模型
有时候我们想知道event_base
底层有哪些模型可用
接口
const char **event_get_supported_methods(void);
这个函数会返回一个数组,里面列出了可用的模型。最后一个指向NULL
表示结束。
示例
int i;
const char **methods = event_get_supported_methods();
printf("Starting Libevent %s. Available methods are:
",
event_get_version());
for (i=0; methods[i] != NULL; ++i) {
printf(" %s
", methods[i]);
}
接口
const char *event_base_get_method(const struct event_base *base);
enum event_method_feature event_base_get_features(const struct event_base *base);
event_base_get_method
获取当前在用的模型。event_base_get_features
获取将来可能支持的模型
示例
struct event_base *base;
enum event_method_feature f;
base = event_base_new();
if (!base) {
puts("Couldn't get an event_base!");
} else {
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("");
}
释放event_base
当我们使用完后需要释放event_base
的资源
void event_base_free(struct event_base *base);
需要注意,这里仅仅是释放event_base
,并不会释放任何与它关联的变量,比如socket,或是一些指针。
为event_base
设置优先级
libevent支持为event设置多个优先级,但是event_base
只能设置一个
int event_base_priority_init(struct event_base *base, int n_priorities);
成功返回0,失败返回-1。event_base
的n_priorities必须是1,而新创建的event可以是0,也可以是-1
EVENT_MAX_PRIORITIES
定义了最高优先级,如果设置过高,会报错
在调用任何event之前设置它的优先级,最好是在创建完成后就设置好。
可以调用int event_base_get_npriorities(struct event_base *base);
获取当前event的优先级。如果返回值是3,就表示0,1,2三个优先级是可用的。
一般情况下,新创建的event的优先级都设置为与它关联的event_base
的二分之一,n_priorities / 2
fork()
之后重新初始化event_base
fork()
之后event_base
的有些信息并没有完全清理,所以需要初始化
int event_reinit(struct event_base *base);
示例
struct event_base *base = event_base_new();
/* ... add some events to the event_base ... */
if (fork()) {
/* In parent */
continue_running_parent(base); /*...*/
} else {
/* In child */
event_reinit(base);
continue_running_child(base); /*...*/
}
废弃的event_base
函数
以前版本的libevent很大程度上,在实现是想上,与当前的event_base
一样。当前的event_base
是一个全局共享的资源。如果没有特殊定义,你是用的是当前的event_base
,记住,event_base
是线程不安全的,不要使用的时候出错。
建议使用struct event_base *event_init(void);
来代替event_base_new()
。
Current function | Obsolete current-base version |
---|---|
event_base_priority_init() | event_priority_init() |
---- | ---- |
event_base_get_method() | event_get_method() |