libevent学习四
来源:程序员人生 发布时间:2015-02-06 08:30:42 阅读次数:5127次
构建event_base
在你开始使用任何Libevent前,你需要先创建1个或多个event_base。每一个event_base管理着1个event的集合,并可以检测出哪些event被激活了。如果event_base使用了锁,就能够在多线程中安全的访问它。但要注意它的主poll函数只能被单个线程运行。如果你想用多个线程运行IO迭代器,你需要为每一个线程分配1个event_base。
注:在以后的版本中,Libevent可能提供对跨线程event的支持。
每一个event_base都有1个“方法”或主IO函数,用来肯定哪些event已被准备好。其中包括:
1. select
2. poll
3. epoll
4. kqueue
5. devpoll
6. evport
7. win32
使用者可以通过环境变量来禁用某个指定的主函数。如果你想去关掉kqueue函数,可以设置EVENT_NOKQUEUE这个环境变量等。如果你想在程序内部关掉,可以看下面对event_config_avoid_method的介绍:
设定默许的event_base
函数event_base_new()会创建1个默许设置的event_base。它根据相应的环境变量,返回1个指向event_base的指针。如果毛病返回NULL。
默许它会自动选择系统所支持的最快的主函数。
接口
struct event_base *event_base_new(void);
对大多数程序,默许的设置就已满足你的需求了。
定制自己的event_base
如果你想定制自己的event_base,你需要使用到event_config。event_config是个不对外开放的结构体。它保存着你对event_base偏好设定的相干信息。你可以通过传入event_config到event_base_new_with_config()来创建event_base。
接口
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_config_new()用来创建1个event_config。然后,调用其他的1些方法去告知它你想要的。最后通过event_base_new_with_config去创建1个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不要使用那些不支持feature所指定功能的主函数。event_config_set_flag()用来告知Libevent在构建event_base的时候去设置1些运行时标志。
1些用在event_config_require_features的feature有:
EV_FEATURE_ET:要求主函数支持edge-triggered。(边沿模式)
EV_FEATURE_O1:要求主函数增加,删除event或某个event被激活的算法复杂度都是O(1)。
EV_FEATURE_FDS:要求主函数可以处理任意的文件描写符,而不单单是socket。
event_config_set_flag使用到的值有:
EVENT_BASE_FLAG_NOLOCK:不给event_base分配锁。设置这个选项可能为你节省1点花费在加锁,解锁上的时间,但是在多线程情况下,会变得不安全。
EVENT_BASE_FLAG_IGNORE_ENV:当选择使用哪一个主函数时,不检查EVENT_*的环境变量。使用前要想清楚,由于它会让你在调试你的程序时,变得困难。
EVENT_BASE_FLAG_STARTUP_IOCP:只用在Windows上,在启动时就启用必要的IOCP逻辑调度。而不是按需。
EVENT_BASE_FLAG_NO_CACHE_TIME:不是在事件循环每次准备履行超时回调时检测当前时间,而是在每次超时回调落后行检测。注意,这会消耗更多的CPU时间。
EVENT_BASE_FLAG_EPOLL_USE_CHANGLIST:告知Libevent,如果使用的主函数是epoll,使用更高效的“changelist”模式。如果同1个fd的状态在进入下1次循环前就被修改,epoll-changelist可以免没必要要的系统调用。但要注意的是,如果Libevent使用的fd被dup()函数克隆,那它可能会触发1个内核毛病。如果你没有使用epoll,它是不起作用的。你可以通过设定EVENT_EPOLL_USE_CHANGLIST环境变量去启用epoll-changelist。
EVENT_BASE_FLAG_PRECISE_TIMER:默许情况下,Libevent会尝试去使用系统提供的效力最高的计时器。如果有1个计时器虽然比较慢,但是有更高的精确度。这个flag会让Libevent去使用它。
接口
int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)
这个方法只在使用IOCP的Windows系统上有作用,固然在将来它也会被用在其他平台上。调用它用来告知event_base充分使用给定数量的CPU。注:这只是1个提议,event_base可能会使用多于或少于你所给定的值。
接口
int event_config_set_max_dispatch_interval(struct event_config *cfg,
const struct timeval *max_interval, int max_callbacks,
int min_priority);
这个方法用来避免优先级反转。它是通过限制在检查高优先级event前,最多允许可被调用的低优先级event的数量来到达目的。如果max_interval非空,事件循环在每次回调后都会检查时间。如果超过max_interval指定的时间,就会重新扫描高优先级的events。
如果max_callbacks非负,在max_callbacks调用被调用后,时间循环会继续检查更多的events。这些规则适用于任何高于min_priority的event。
例子:避免优先级反转
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⑴ 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);
检查某个event_base的主函数
某些时候,你想肯定1个event_base的支持那些特性,或它使用的是哪一个主函数,你可以:
接口
const char **event_get_supported_methods(void);
它返回1个指向方法名的数组的指针。最后1个元素为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里面的主函数名字。event_base_get_features()函数它所支持的feature的按位与。
例:
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的时候,你可以通过调用event_base_free()释放它。
接口
void event_base_free(struct event_base *base);
注:这个方法其实不释放任何event_base所管理的events,也不会关闭它们的socket,或释放它们的指针。
设置event_base的优先级
Libevent支持在1个event_base中设置多个优先级。默许它只支持1个基本的优先级。你可以通过调用event_base_priority_init()来设置优先级的数量。
接口
int event_base_priority_init(struct event_base *base, int n_priorities);
它成功返回0,失败返回⑴。n_priorities是所设置的优先级的个数。它最小为1。可用的优先级是从0(优先级最高)到n_priorities⑴(优先级最低)。
它有个最大常量的限制EVENT_MAX_PRIORITIES。
注:这个方法必须在任何events被激活前被调用。最好在创建event_base后就调用它。
查看当前的event_base支持多少个优先级。可用调用:
接口
int event_base_get_npriorities(struct event_base *base);
默许情况下,所有新的event被分配的优先级会被初始化为当前event_base的n_priorities/2。
fork()后重新初始化event_base
在调用fork()后,event相干的数据可能变脏。所以,如果你想在fork后的进程中继续使用event_base,你需要去重新初始化它。
接口
int event_reinit(struct event_base *base);
这个方法成功返回0,失败返回⑴。
例:
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); /*...*/
}
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠