您的位置:首页 > 其它

LibEvent中文帮助文档-第8章【处理事件】

2016-12-02 13:32 453 查看

LiEvent中文帮助文档--第8章【创建事件】

   返回主目录

Libevent

快速可移植非阻塞式网络编程
 
 

修订历史
版本
日期
作者
备注
V1.0
2016-11-15
周勇
Libevent编程中文帮助文档
 

文档是2009-2012年由Nick-Mathewson基于Attribution-Noncommercial-Share
Alike许可协议3.0创建,未来版本将会使用约束性更低的许可来创建.
此外,本文档的源代码示例也是基于BSD的"3条款"或"修改"条款.详情请参考BSD文件全部条款.本文档最新下载地址:
英文:http://libevent.org/
中文:http://blog.csdn.net/zhouyongku/article/details/53431750
请下载并运行"gitclonegit://github.com/nmathewson/libevent-
book.git"获取本文档描述的最新版本源码.

<<上一章>>

8.处理事件

libevent的基本操作单元是事件.每个事件代表一组条件的集合,这些条件包括:

文件描述符已经就绪,可以读取或者写入

文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)

超时事件

发生某信号

用户触发事件

 

所有事件具有相似的生命周期.调用 libevent函数设置事件并且关联到 event_base之后 ,事件进入"已初始化(initialized)"状态.此时可以将事件添加到 event_base中,这使之进入"未决(pending)"状态.在未决状态下,如果触发事件的条件发生(比如说,文件描述符的状态改变,或者超时时间到达),则事件进入"激活(active)"状态,(用户提供的)事件回调函数将被执行.如果配置为"持久的(persistent)",事件将保持为未决状态.否则 ,执行完回调后,事件不再是未决的.删除操作可以让未决事件成为非未决(已初始化)的 ;添加操作可以让非未决事件再次成为未决的.

 

8.1构造事件对象

使用 event_new()接口创建事件.
 
接口

#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20
typedef void ( * event_callback_fn)(evutil_socket_t, short, void* );
struct event* event_new(struct event_base * base, evutil_socket_t fd,
short what, event_callback_fn cb,void* arg);
void event_free(struct event* event);

event_new()试图分配和构造一个用于base的新的事件.
what参数是上述标志的集合.如果fd非负,则它是将被观察其读写事件的文件.事件被激活时,
libevent将调用cb函数,传递这些参数:文件描述符fd,表示所有被触发事件的位字段,以及构造事件时的arg参数.发生内部错误,或者传入无效参数时,
event_new()将返回NULL.所有新创建的事件都处于已初始化和非未决状态,调用event_add
()可以使其成为未决的.要释放事件,调用event_free().对未决或者激活状态的事件调用event_free()是安全的:在释放事件之前,函数将会使事件成为非激活和非未决的 .

接口
#include <event2/event.h>
void cb_func(evutil_socket_t fd, short what, void* arg)
{
const char* data = arg;
printf("Got an event on socket %d:%s%s%s%s [%s]",
(int) fd,
(what&EV_TIMEOUT) ? " timeout" : "",
(what&EV_READ) ? " read" : "",
(what&EV_WRITE) ? " write" : "",
(what&EV_SIGNAL) ? " signal" : "",
data);
}
void main_loop(evutil_socket_t fd1, evutil_socket_t fd2)
{
struct event* ev1, * ev2;
struct timeval five_seconds = {5,0};
struct event_base
* base = event_base_new();
/* The caller has already set up fd1, fd2 somehow, and make them nonblocking.*/
ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func,
(char * )"Reading event");
ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func,
(char * )"Writing event");
event_add(ev1, &five_seconds);
event_add(ev2, NULL);
event_base_dispatch(base);
}

述函数定义在<event2/event.h>中,首次出现在 libevent
2.0.1-alpha 版本中.event_callback_fn类型首次在2.0.4-alpha版本中作为typedef出现.
 

8.1.1事件标志

EV_TIMEOUT:这个标志表示某超时时间流逝后事件成为激活的.构造事件的时候,
EV_TIMEOUT标志是被忽略的: 可以在添加事件的时候设置超时,也可以不设置.超时发生时,回调函数的what参数将带有这个标志.

EV_READ:表示指定的文件描述符已经就绪,可以读取的时候,事件将成为激活的.

EV_WRITE:表示指定的文件描述符已经就绪,可以写入的时候,事件将成为激活的.

EV_SIGNAL:用于实现信号检测,请看下面的"构造信号事件"节.

EV_PERSIST:表示事件是"持久的",请看下面的"关于事件持久性"节.

EV_ET:表示如果底层的event_base后端支持边沿触发事件,则事件应该是边沿触发的.这个标志影响EV_READ和EV_WRITE的语义.

从2.0.1-alpha版本开始,可以有任意多个事件因为同样的条件而未决.比如说,可以有两个事件因为某个给定的fd已经就绪,可以读取而成为激活的.这种情况下,多个事件回调被执行的次序是不确定的.

这些标志定义在<event2/event.h>中.除了EV_ET在2.0.1-alpha版本中引入外,所有标志从1.0版本开始就存在了.

8.1.2关于事件持久性

默认情况下,每当未决事件成为激活的 ( 因为fd已经准备好读取或者写入,或者因为超时),事件将在其回调被执行前成为非未决的.如果想让事件再次成为未决的,可以在回调函数中再次对其调用event_add( ).
 
如果设置了EV_PERSIST标志,事件就是持久的.这意味着即使其回调被激活,事件还是会保持为未决状态.如果想在回调中让事件成为非未决的,可以对其调用event_del( ).每次执行事件回调的时候,持久事件的超时值会被复位.
 
因此,如果具有EV_READ|EV_PERSIST标志,以及5秒的超时值,则事件将在以下情况下成为激活的:

套接字已经准备好被读取

从最后一次成为激活的开始,已经逝去5秒

8.1.3创建一个用本身作为回调函数参数的event

很多情况下你想要创建一个event并且用自身作为回调函数参数.不能只传一个指向event的指针作为参数到函数event_new(),因为它还未实际存在,为了解决这个问题,可以使用event_self_cbkarg()函数.
 
接口
void* event_self_cbarg();

event_self_cbarg()函数返回了一个传入到event回调函数作为参数的魔幻指针,该指针告诉event_new()去创建一个event并且把自身作为回调函数参数.
示例
#include <event2/event.h>
static int n_calls = 0;
void cb_func(evutil_socket_t fd, short what, void* arg)
{
struct event* me = arg;
printf("cb_func called %d times so far.\n", ++n_calls);
if (n_calls > 100)
event_del(me);
}
void run(struct event_base* base)
{
struct timeval one_sec = { 1, 0 };
struct event* ev;
/* We’re going to set up a repeating timer to get called called 100 times.*/
ev = event_new(base, -1, EV_PERSIST, cb_func, event_self_cbarg());
event_add(ev, &one_sec);
event_base_dispatch(base);
}

这个函数也可以用于 event_new(), evtimer_new(), evsignal_new(), event_assign(),
evtimer_assign(),以及 evsig-nal_assign(),但不会作为non-events的回调函数的参数.
 
event_self_cbarg()函数出现在LibEvent-2.1.2-alpha中.
 

8.1.4超时事件

相对于之前的方式,有一系列的宏以evtimer_开头的宏可以用来分配和控制纯超时的event.这些宏使代码更加清晰,除此之外别无其它作用.
 
接口
#define evtimer_new(base, callback, arg) \
event_new((base), -1, 0, (callback), (arg))
#define evtimer_add(ev, tv) \
event_add((ev),(tv))
#define evtimer_del(ev) \
event_del(ev)
#define evtimer_pending(ev, tv_out) \
event_pending((ev), EV_TIMEOUT, (tv_out))

除了evtimer_new()出现在LibEvent2.0.1-alpha,其它这些宏从LibEvent0.6开始出现.
 

8.1.5构造信号事件

LibEvent也支持监控POSIX格式的信号.构造一个信号句柄,使用下面的方法:
示例
struct event* hup_event;
struct event_base* base = event_base_new();
/* call sighup_function on a HUP signal*/
hup_event = evsignal_new(base, SIGHUP, sighup_function, NULL);

注意:信号回调函数在信号发生后在运行在event循环中运行,因此对他们来说调用那些你不应该调用的标准POSIX信号句柄是安全的.
____________________________________________________________________________________________________
注意
不要给一个event设置超时.这可能是不支持的(待修正:真是这样吗?)
____________________________________________________________________________________________________

同样有一系列很便利的宏配合信号event使用.

接口

#define evsignal_add(ev, tv) \
event_add((ev),(tv))
#define evsignal_del(ev) \
event_del(ev)
#define evsignal_pending(ev, what, tv_out) \
event_pending((ev), (what), (tv_out))

evsignal_*宏在LibEvent2.0.1-alpha中出现,在此之前使用如signal_add()、signal_del()等函数.
 
使用信号时的警告
当前版本的信号大多数运行在后台,每个进程只能有一个event_base可以同时监控信号,如果同时添加两个event到event_base,即使信号是不同的,也只会有一个event_base会接收到信号.
 
后台运行的kqueue没有这个限制.

8.1.6不用堆分配来设置事件

对于性能或者别的原因,人们习惯于分配event作为大结构体的一部分.对于每次event使用,是这样保存event的:

内存分配器在堆上分配一个小的对象的开销.

从指向event的指针取值的时间开销.

由于event不在缓存中造成缓存丢失而引起的时间开销.

这种方式可能会破坏LibEvent版本之间的二进制兼容性,因为版本间的event结构体大小不统一.
 
这种方式开销很小,也不会影响其他程序运行.你应该坚持使用event_new()来分配event除非你在堆上分配event的时候遭受了很严重的性能问题.如果以后的版本使用的event结构体比你当前使用的event结构体大那么使用event_assign()会导致很难排查的错误.
 
接口
int event_assign( struct event* event,
struct event_base * base,
evutil_socket_t fd,
short what,
void (*callback)(evutil_socket_t, short, void*),
void * arg);

除了 event参数必须指向一个未初始化的事件之外,event_assign()的参数与 event_new()的参数相同.成功时函数返回0,如果发生内部错误或者使用错误的参数,函数返回-1.
 
示例
#include <event2/event.h>
/* Watch out! Including event_struct.h means that your code will
not be binary-compatible with future versions of Libevent.*/
#include <event2/event_struct.h>
#include <stdlib.h>
struct event_pair
{
evutil_socket_t fd;
struct event read_event;
struct event write_event;
};
void readcb(evutil_socket_t, short, void* );
void writecb(evutil_socket_t, short, void* );
struct event_pair* event_pair_new(struct event_base * base, evutil_socket_t fd)
{
struct event_pair* p = malloc(sizeof(struct event_pair));
if (!p) return NULL;
p->fd = fd;
event_assign(&p->read_event, base, fd, EV_READ|EV_PERSIST, readcb, p);
event_assign(&p->write_event, base, fd, EV_WRITE|EV_PERSIST, writecb, p);
return p;
}

也可以用event_assign()去初始化在栈上分配或静态分配的event.
 
____________________________________________________________________________________________________
警告

不要对已经在 event_base中未决的事件调用 event_assign(),这可能会导致难以诊断的错误.如果已经初始化和成为未决的,调用 event_assign()之前需要调用 event_del().libevent提供了方便的宏将 event_assign()用于仅超时事件或者信号事件.
 
接口
size_t event_get_struct_event_size(void);

该函数返回了需要设置的event结构体的字节数.在此之前,如果你认识到为你的程序在堆上内存分配实际上是一个很重要的问题,你可能仅仅会使用这个函数,因为这会使你的程序更加难以读写.
 
注意,event_get_struct_event_size()在将来版本可能会给出比当前sizeof(struct
event)更大的值,如果是这样,那么这就意味着event字节末尾的额外字节是预留的填充字节,以便未来的LibEvent版本使用.
 
这里也有一个和上面类似的例子,不同的是它不依赖于sizeof来计算struct.h中的event结构体的字节大小,而是使用event_get_struct_event_size()来运行时计算正确的值.
 
接口
#include <event2/event.h>
#include <stdlib.h>
/* When we allocate an event_pair in memory, we’ll actually allocate
more space at the end of the structure. We define some macros
to make accessing those events less error-prone.*/
struct event_pair
{
evutil_socket_t fd;
};
/* Macro: yield the struct event ’offset’ bytes from the start of ’p’*/
#define EVENT_AT_OFFSET(p, offset) \
((struct event * ) ( ((char * )(p)) + (offset) ))
/* Macro: yield the read event of an event_pair*/
#define READEV_PTR(pair) \
EVENT_AT_OFFSET((pair), sizeof(struct event_pair))
/* Macro: yield the write event of an event_pair*/
#define WRITEEV_PTR(pair) \
EVENT_AT_OFFSET((pair), \
sizeof(struct event_pair)+event_get_struct_event_size())
/* Macro: yield the actual size to allocate for an event_pair*/
#define EVENT_PAIR_SIZE() \
(sizeof(struct event_pair)+2 * event_get_struct_event_size())
void readcb(evutil_socket_t, short, void* );
void writecb(evutil_socket_t, short, void* );
struct event_pair* event_pair_new(struct event_base * base, evutil_socket_t fd)
{
struct event_pair* p = malloc(EVENT_PAIR_SIZE());
if (!p) return NULL;
p->fd = fd;
event_assign(READEV_PTR(p), base, fd, EV_READ|EV_PERSIST, readcb, p);
event_assign(WRITEEV_PTR(p), base, fd, EV_WRITE|EV_PERSIST, writecb, p);
return p;
}

从LibEvent2.0.1-alpha版本开始,event_assign()函数就定义在<event2/event.h>中.从2.0.3开始该函数返回了一个int,在此之前都是无返回值的.event_get_struct_size()函数首次出现在LibEvent2.0.4-alpha版本中.event结构体定义在<event2/event.h>文件中.
 

8.2使事件未决和非未决

一旦你构造了一个event,它是不会做任何事的直到你add它使之未决.event_add这样做:
 
接口
int event_add(struct event* ev, const struct timeval * tv);

在非未决的事件上调用 event_add()将使其在配置的
event_base 中成为未决的.成功时函数返回0,失败时返回-1.如果
tv 为 NULL,添加的事件不会超时.否则,tv以秒和微秒指定超时值.如果对已经未决的事件调用
event_add() ,事件将保持未决状态,并在指定的超时时间被重新调度.
____________________________________________________________________________________________________
注意
不要设置 tv为
希 望 超 时 事 件 执 行 的 时 间.如 果 在2010年1月1
日 设 置"tv->tv_sec=time(NULL)+10;",超时事件将会等待40年,而不是10秒.
____________________________________________________________________________________________________


接口
int event_del(struct event* ev);

对已经初始化的事件调用 event_del()将使其成为非未决和非激活的.如果事件不是未决的或者激活的,调用将没有效果.成功时函数返回0,失败时返回-1.
____________________________________________________________________________________________________
注意
如果在事件激活后,其回调被执行前删除事件,回调将不会执行.
____________________________________________________________________________________________________

接口
int event_remove_timer(struct event* ev);


最后你可以移除一个event的超时而不再需要删除IO或信号组件了.如果event没有超时未决,event_remove_timer()函数调用不会产生影响.如果event只有超时但是没有信号组件,event_remove_timer()与event_del()有相同的作用.函数成功返回0,失败返回-1.
 
这些函数定义在<event2/event.h>中,event_add()和event_del()函数在LibEvent0.1就已经有了,evet_remove_timer()在2.1.2-alpha版本才开始被加入进来.
 

8.3事件优先级

当多个event同时触发,LibEvent并没有定义回掉函数执行要遵循的任何顺序.可以用优先级来定义一些event比其他event更加重要.
 
在之前部分的讨论中我们知道event_base有一个或多个优先级关联到它.在添加event到event_base之前在初始化它之后,你可以设置它的优先级.
 
接口
int event_priority_set(struct event* event, int priority);

event的优先级是一个在0和优先级总数之间的一个值,最小为1.函数成功返回0失败返回-1.
 
但多个事件的多个优先级开始活动,低优先级的不会被执行,而回执行高优先级的event,然后再检查event的优先级,再来一次,直到高优先级的事件都没有活动则才开始运行低优先级的event.
 
接口
#include <event2/event.h>
void read_cb(evutil_socket_t, short, void* );
void write_cb(evutil_socket_t, short, void* );
void main_loop(evutil_socket_t fd)
{
struct event* important, * unimportant;
struct event_base* base;
base = event_base_new();
event_base_priority_init(base, 2);
/* Now base has priority 0, and priority 1*/
important = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL);
unimportant = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL);
event_priority_set(important, 0);
event_priority_set(unimportant, 1);
/* Now, whenever the fd is ready for writing, the write callback will
happen before the read callback. The read callback won’t happen at
all until the write callback is no longer active.*/
}

当你不设置event的优先级,默认值是优先级列表总数除以2.
函数定义在<event2/event.h>中,首次出现在LibEvent1.0.
 

8.4检查事件状态

有时候你想要判断一个event是否被添加,判断它引用的是什么.
 
接口
int event_pending(const struct event* ev, short what, struct timeval * tv_out);
#define event_get_signal(ev) /* ...*/
evutil_socket_t event_get_fd(const struct event* ev);
struct event_base* event_get_base(const struct event * ev);
short event_get_events(const struct event* ev);
event_callback_fn event_get_callback(const struct event* ev);
void* event_get_callback_arg(const struct event * ev);
int event_get_priority(const struct event* ev);
void event_get_assignment( const struct event* event,
struct event_base** base_out,
evutil_socket_t* fd_out,
short* events_out,
event_callback_fn* callback_out,
void** arg_out);

event_pending函数确定给出的event是未决的还是活动的.如果EV_READ、EV_WRITE、EV_SIGNAL、EV_TIMEOUT被设置为what参数,函数会返回event是未决的或者活动的所有标志.如果提供了tv_out并且设置了EV_TIMEOUT标志给what参数,当前event是未决的或者活跃在超时上,tv_out设置为保存event超时后的时间.
 
event_get_fd()函数和event_get_signal()函数返回了event配置的文件描述符或者型号数量.event_get_base()返回event配置的event_base.event_get_events()函数返回事件的标志(EV_READ、EV_WRITE等).event_get_callback()和event_get_callback_arg()函数返回了event的回掉函数和它的参数指针.event_get_priority()函数返回了事件当前分配的优先级.
 
event_get_assignment()函数拷贝了event分配的所有字段到提供的指针.如果指针为空,则忽略.
 
示例
#include <event2/event.h>
#include <stdio.h>
/* Change the callback and callback_arg of ‘ev’, which must not be pending.*/
int replace_callback(struct event* ev,
event_callback_fn new_callback,
void*new_callback_arg)
{
struct event_base* base;
evutil_socket_t fd;
short events;
int pending;
pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,NULL);
if (pending)
{
/* We want to catch this here so that we do not re-assign a
pending event. That would be very very bad.*/
fprintf(stderr,"Error! replace_callback called on a pending
event!\n");
return -1;
}
event_get_assignment(ev,
&base,
&fd,
&events,
NULL /* ignore old callback*/ ,
NULL /* ignore old callback argument*/);
event_assign(ev, base, fd, events, new_callback, new_callback_arg);
return 0;
}

这些函数声明在<event2/event.h>中. event_pending ()函数从0.1版就存在了.
2.0.1-alpha版引入了event_get_fd()和event_get_signal() .2.0.2-alpha引入了event_get_base()
.其他的函数在2.0.4-alpha版中引入.
 

8.5查找当前运行事件

为了调试或者别的目的,你可以获取一个指向当前运行event的指针.
 
接口
struct event* event_base_get_running_event(struct event_base * base);

注意这个函数的行为仅仅在提供的event_base的循环内部调用的时候被定义.在别的线程调用它是不支持的,可能会引起未定义的行为发生.
 
该函数定义在<event2/event.h>中,首次出现在LibEvent2.1.1-alpha版本中.
 

8.6配置一次性事件

如果不想重复添加event或者添加后立马删除,它不需要是持久的,你可以使用event_base_once()函数.
 
接口
int event_base_once( struct event_base* ,
evutil_socket_t, short,
void ( * )(evutil_socket_t, short, void* ),
void * ,
const struct timeval * );

该函数接口与event_new()相同,除了不支持EV_SIGNAL或SIG_PERSIST标志.队列event以默认优先级被插入和运行.当回掉函数最终完成,LibEvent释放它自己内部的event结构体,成功返回0,失败返回-1.
 
以event_base_once插入的event不能删除或手动激活:如果你要使能或取消一个event,用常用的event_new()或event_assign()接口来创建.
 
注意在LibEvent2.0之前,如果event从来不被触发,用来保存它的内存将永远不会释放.从LibEvent2.1.2-alpha版本开始,当event_base释放的时候释放event,即便它们没有激活,但是还是要意识到:如果有内存关联到它们的回掉函数参数,除非你的程序做点事情去追踪它或者释放它否则你的内存是不会释放掉的.
 

8.7手动激活事件

可能会有很少的这种情况发生:即便event的条件没有触发也想要event激活.
 
接口
void event_active(struct event* ev, int what, short ncalls);

该函数使event以标志what(EV_READ、EV_WRITE、EV_TIMEOUT的组合)激活,event不需要预先的被未决,激活event也不需要使其未决.
 
糟糕的示例:用event_active()无限循环
struct event* ev;
static void cb(int sock, short which, void* arg)
{
/* Whoops: Calling event_active on the same event unconditionally
from within its callback means that no other events might not getrun!*/
event_active(ev, EV_WRITE, 0);
}
int main(int argc, char** argv)
{
struct event_base
* base = event_base_new();
ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL);
event_add(ev, NULL);
event_active(ev, EV_WRITE, 0);
event_base_loop(base, 0);
return 0;
}

这将创建一个只执行一次的事件循环,然后永远调用函数"cb".
 
示例:上述问题的计时器替代方案
struct event* ev;
struct timeval tv;
static void cb(int sock, short which, void* arg)
{
if (!evtimer_pending(ev, NULL))
{
event_del(ev);
evtimer_add(ev, &tv);
}
}
int main(int argc, char** argv)
{
struct event_base
* base = event_base_new();
tv.tv_sec = 0;
tv.tv_usec = 0;
ev = evtimer_new(base, cb, NULL);
evtimer_add(ev, &tv);
event_base_loop(base, 0);
return 0;
}

示例:上述问题的event_config_set_max_dispatch_interval替代方案
struct event* ev;
static void cb(int sock, short which, void* arg)
{
event_active(ev, EV_WRITE, 0);
}
int main(int argc, char** argv)
{
struct event_config* cfg = event_config_new();
/* Run at most 16 callbacks before checking for other events.*/
event_config_set_max_dispatch_interval(cfg, NULL, 16, 0);
struct event_base
* base = event_base_new_with_config(cfg);
ev = event_new(base, -1, EV_PERSIST | EV_READ, cb, NULL);
event_add(ev, NULL);
event_active(ev, EV_WRITE, 0);
event_base_loop(base, 0);
return 0;
}

这个函数定义在<event2/event.h>中,从0.3版本就存在了.
 

8.8优化通用超时

当前版本的 libevent使用二进制堆算法跟踪未决事件的超时值,这让添加和删除事件超时值具有O(logN)性能.对于随机分布的超时值集合,这是优化的,但对于大量具有相同超时值的事件集合,则不是.
 
比如说,假定有10000个事件,每个都需要在添加后5秒触发超时事件.这种情况下,使用双链队列实现才可以取得O(1)性能.
 
实际上,不希望为所有超时值使用队列,因为队列仅对常量超时值更快.如果超时值或多或少地随机分布,则向队列添加超时值的性能将是O(n)
,这显然比使用二进制堆糟糕得多.
 
libevent通过放置一些超时值到队列中,另一些到二进制堆中来解决这个问题.要使用这个机制,需要向libevent请求一个"公用超时(common
timeout)"值,然后使用它来添加事件.如果有大量具有单个公用超时值的事件,使用这个优化应该可以改进超时处理性能.
 
接口
const struct timeval* event_base_init_common_timeout(
struct event_base* base,
const struct timeval * duration);

这个函数需要 event_base和要初始化的公用超时值作为参数.函数返回一个到特别的timeval结构体的指针,可以使用这个指针指示事件应该被添加到O(1)队列,而不是O(logN)堆.可以在代码中自由地复制这个特别的timeval或者进行赋值,但它仅对用于构造它的特定event_base有效.不能依赖于其实际内容:libevent使用这个内容来告知自身使用哪个队列.
 
示例
#include <event2/event.h>
#include <string.h>
/* We’re going to create a very large number of events on a given base,
nearly all of which have a ten-second timeout. If initialize_timeout
is called, we’ll tell Libevent to add the ten-second ones to an O(1) queue.*/
struct timeval ten_seconds = { 10, 0 };
void initialize_timeout(struct event_base* base)
{
struct timeval tv_in = { 10, 0 };
const struct timeval
* tv_out;
tv_out = event_base_init_common_timeout(base, &tv_in);
memcpy(&ten_seconds, tv_out, sizeof(struct timeval));
}
int my_event_add(struct event* ev, const struct timeval * tv)
{
/* Note that ev must have the same event_base that we passed to
initialize_timeout
*/
if (tv && tv->tv_sec == 10 && tv->tv_usec == 0)
return event_add(ev, &ten_seconds);
else
return event_add(ev, tv);
}

与所有优化函数一样,除非确信适合使用,应该避免使用公用超时功能.这个函数由2.0.4-alpha版本引入.
 

8.9从已清除的内存中识别事件

libevent提供了函数,可以从已经通过设置为0(比如说,通过calloc()分配的,或者使用memset()或者bzero()清除了的)而清除的内存识别出已初始化的事件.
 
接口
int event_initialized(const struct event* ev);
#define evsignal_initialized(ev) event_initialized(ev)
#define evtimer_initialized(ev) event_initialized(ev)

警告这个函数不能可靠地从没有初始化的内存块中识别出已经初始化的事件.除非知道被查询的内存要么是已清除的,要么是已经初始化为事件的,才能使用这个函数.
 
除非编写一个非常特别的应用,通常不需要使用这个函数.event_new()返回的事件总是已经初始化的.
 
接口
#include <event2/event.h>
#include <stdlib.h>
struct reader
{
evutil_socket_t fd;
};
#define READER_ACTUAL_SIZE() \
(sizeof(struct reader) + \
event_get_struct_event_size())
#define READER_EVENT_PTR(r) \
((struct event* ) (((char * )(r))+sizeof(struct reader)))

struct reader* allocate_reader(evutil_socket_t fd)
{
struct reader* r = calloc(1, READER_ACTUAL_SIZE());
if (r)
r->fd = fd;
return r;
}
void readcb(evutil_socket_t, short, void* );
int add_reader(struct reader* r, struct event_base * b)
{
struct event* ev = READER_EVENT_PTR(r);
if (!event_initialized(ev))
event_assign(ev, b, r->fd, EV_READ, readcb, r);
return event_add(ev, NULL);
}

event_initialized()函数从0.3版本就存在了.
 

8.10废弃的事件操作函数

2.0版本之前的libevent没有event_assign()或者event_new()
.替代的是将事件关联到"当 前"event_base的event_set
( ) .如 果 有 多 个event_base ,需 要 记 得 调 用event_base_set()来确定事件确实是关联到当前使用的event_base的.
 
接口
void event_set(struct event* event,
evutil_socket_t fd, short what,
void( * callback)(evutil_socket_t, short, void* ),
void * arg);
int event_base_set(struct event_base* base, struct event * event);

除了使用当前 event_base之外, event_set ()跟event_assign
()是相似的.
 
event_base_set()用于修改事件所关联到的event_base.event_set()具有一些用于更方便地处理定时器和信号的变体:evtimer_set()大致对应evtimer_assign()
.
 
2.0版本之前的libevent使用"signal_"作为用于信号的event_set()等函数变体的前缀,而不是"evsignal_"(也就是说,有signal_set()、signal_add()、signal_del()、signal_pending()和signal_initialized())
.远古版本(0.6版之前)的libevent使用"timeout_"而不是"evtimer_"
.因此,做代码考古(code archeology) (注:这个翻译似乎不正确,是否有更专业的术语?比如说,"代码复审")时可能会看到timeout_add()、timeout_del()、timeout_initialized()、timeout_set()和timeout_pending()等等.
 
较老版本(2.0版之前)的libevent用宏EVENT_FD()和EVENT_SIGNAL()代替现在的event_get_fd()和event_get_signal()函数.这两个宏直接检查event结构体的内容,所以会妨碍不同版本之间的二进制兼容性.在2.0以及后续版本中,这两个宏仅仅是event_get_fd()和event_get_signal()的别名.
 
由于2.0之前的版本不支持锁,所以在运行event_base的线程之外的任何线程调用修改事件状态的函数都是不安全的.这些函数包括event_add()、event_del()、event_active()和event_base_once().
 
有一个 event_once()与event_base_once()相似,只是用于当前event_base.
 
2.0版本之前EV_PERSIST标志不能正确地操作超时.标志不会在事件激活时复位超时值,而是没有任何操作.
 
2.0之前的版本不支持同时添加多个带有相同fd和READ/WRITE标志的事件.也就是说,在每个fd上,某时刻只能有一个事件等待读取,也只能有一个事件等待写入.

<<下一章>>

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: