一个event_base就是一个Reactor框架。我们在调用任何Libevent的函数前,我们都是需要先申请 event_base 结构体。对于一个event_base结构来说,它会保存一系列的event事件并且以轮训的方式去关注每一个事件,去查看哪一个事件是就绪的。(事件就绪可以从unp第6章 I/O模型中有讲到)
如果一个 event_base设置了使用lock,那么它在多线程之间访问就是安全的。虽然event_base 可以在多线程中使用,但是一个loop仅仅只能在一个线程内。如果我们想多个线程都去轮训,那么我们只能每一个线程去申请一个event_base。
Libevent是基于Reactor模式的一个网络库,也是基于多路复用的一个网络库。所以每一个event_base都有一个对应的多路复用的method,event_base支持的所有method如下:
我们可以取消(disable)指定的method通过环境变量,比如设置 EVENT_NOQUEUE 环境变量。也可以通过调用event_config_avoid_method函数来关闭相应的method。(下面有这个函数介绍)
struct event_base * event_base_new (void)
当我们调用event_base_new函数的时候,这个函数会检测环境变量然后去申请一个相应配置的event_base返回回来。在构造的时候它选择的method是当前OS支持最快的method。大多数场景,我们都是直接调用这个函数来申请event_base的。
struct event_base *event_init(void)
下面是Libevent对event_init 的源码实现
struct event_base *event_init(void)
{
struct event_base *base = event_base_new();
if (base != NULL)
current_base = base;
return (base);
}
申请多个 event_base的demo
threads[THREADMAX];
for( int i = 0 ; i < THREADMAX; ++i)
{
threads[i].base_ = event_init();
}
Libevent有一个全局的eventbase叫做current_base变量。每次调用event_init的时候,都会为这个全局变量申请一个新的event_base。有的时候我们可以通过这个方式去申请多个event_base如上面的代码列子。
对于current_base变量,Libevent有一系列的操作函数,它们默认的目标都是current_base。
Current function | Obsolete current-base version |
---|---|
event_base_priority_init() | event_priority_init() |
event_base_get_method() | event_get_method() |
它是一个黑盒(不透明)的数据结构,我们并不能直接操作它的成员,我们只能靠相应的函数来操作它
它返回一个event_config 结构体
使用完 config后,我们要释放所以调用这个函数
这个函数就是我们上面提到的去关闭或者禁止某个method的函数
enum event_method_feature {
EV_FEATURE_ET = 0x01,//需要一个支持ET模式的method
EV_FEATURE_O1 = 0x02,//需要一个支持 O(1)去操作每个event事件的method
EV_FEATURE_FDS = 0x04,//需要一个支持任意文件描述符的method(不只socket)
};
enum event_base_config_flag {
EVENT_BASE_FLAG_NOLOCK = 0x01, //它可以提高访问event_base的速度,但是会
//导致线程安全问题
EVENT_BASE_FLAG_IGNORE_ENV = 0x02,//这个标志表示在选择method的时候忽略设
//置的环境变量,一般很少使用,它会增加debug难度 2.0.2版本
EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,//仅在windows下开启IOCP
EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,//默认检测当前时间的时机是当eventloop
//在运行相应的回调函数前,它会去检测更新对应的callback函数的当前时间。开启这个
//选项后,不将是只更新将要运行的callback的当前时间。还要去更新剩下其他所有的活
//跃事件的的callback函数时间,这个选项会增加CPU利用率,谨慎使用
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,//以 Changlist的方式使用epoll
//它可以减少系统调用,但是当fd是dup复制的时候这个选项可能会造成内核BUG!!!
EVENT_BASE_FLAG_PRECISE_TIMER = 0x20//让Libevent使用更精密的计时机制2.1.2
};
以上三个操作 event_config的函数都是0成功,-1失败。
这个函数只能在windows下当使用IOCP的时候调用,调用它可以设置使用多线程的时候,尽可能分配的核数, 2.0.7版本
这个函数可以反正优先级逆制,第二个参数表示过多少时间周期去再一次检测高级的event,max_callback在检测前最多调用多少次这个低优先级的callback后去检测,第三个指对应的低优先级数, 2.1.1-alpha
很简单就是构造event_base
释放event_base
默认event都是一个优先级,但是Libevent运行我们设计多优先级的event事件。优先级默认是从 0 ~ n_priorities-1 ,越小优先级越高
必须在任意一个event成为活跃前就调用这个函数来设置优先级,最好在刚创建event_base后就初始化优先级,n_priorities 必须小于 EVENT_MAX_PRIORITIES。
默认注册到event_base中的event优先级都是 n_priorities / 2.
直接设置优先级
并不是所有的method在fork之后,都能保持之前的event具有持久态,如果我们想fork之后使它之前的event的持久态仍然有效,需调重启函数
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);
返回的一个字符串数组,最后一个是空指针
int i;
const char **methods = event_get_supported_methods();
printf("Starting Libevent %s. Available methods are:\n",
event_get_version());
for (i=0; methods[i] != NULL; ++i) {
printf(" %s\n", methods[i]);
}
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("");
}
struct event_base *base = event_base_new();
/* ... add some events to the event_base ... */
if (fork()) {
/* In parent */
doSomething
} else {
/* In child */
event_reinit(base);
doSomething
}