Libevent:5events相关

         Libevents的基本操作单元是event,每一个event代表了一些条件的集合,这些条件包括:

文件描述符已经准备好读或写

文件描述符正在变为就绪,准备好读或写(仅限于边沿触发)

超时事件

信号发生

用户触发事件

 

         events都有类似的生命周期。一旦调用Libevent函数创建好event,并将其关联到一个event_base之后,他就是“已初始化”状态(initialized)。这种状态下,可以进行add操作,将其状态变为base中的“挂起”状态(pending),处于“挂起”状态的event,如果触发事件的条件发生了(比如,文件描述符的状态发生变化,或者超时了),那么event的状态变为“激活”状态(active),然后它的回调函数(用户提供)开始运行。如果该event配置了“持久”属性(persistent),那么它的状态依然保持为“挂起”,否则,在回调函数运行时,它的状态就不再是“挂起”(“非挂起”状态)。可以通过delete操作,将一个“挂起”状态的event变为“非挂起”状态(non-pending),或者通过add操作,将“非挂起”的event变为“挂起”状态。

 

一:构建event对象

         创建新的event,可以使用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,

                                                    shortwhat,  event_callback_fn  cb,

                                                    void* arg);

 

void  event_free(struct  event * event);

         event_new函数分配并且创建一个新的event对象,并与base进行关联。what参数是上面列出标志的集合,它们的具体意义见下方。如果fd是非负的整数,则它代表了我们需要观察可读或可写事件的文件。当event变为激活时,Libevent就会调用回调函数cb,将文件描述符参数fd,所有触发事件的标志位域,以及event_new的最后一个参数:arg传递个cb。

         如果发生了内部错误,或者参数非法,则event_new返回NULL。

         所有新的events都是“已初始化”和“非挂起”状态,可以调用event_add函数将这样的event变为“挂起”状态。

         调用event_free可以销毁event。对“挂起”或“激活”状态的event调用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>文件中定义。

 

event标志:

EV_TIMEOUT:

         该标志表明,超时时间过后,该event变为“激活”状态。(注意:在构建event时,EV_TIMEOUT标志是被忽略的:当add event时可以设置超时时间,也可以不设置。当超时发生时,回调函数的what参数将会设置该标志。)

EV_READ

         该标志表明,当文件描述符准备好读时,event将会变为“激活”

EV_WRITE

         该标志表明,当文件描述符准备好写时,event将会变为“激活”

EV_SIGNAL

         用来实现信号探测,参见下面的“构造信号事件”

EV_PERSIST:

         标志该event具有“持久”属性,参见下面的“事件持久性”

EV_ET:

         指明如果event_base的底层方法支持“边沿触发”的话,那么该event应该是边沿触发的。这将会影响到EV_READ和EV_WRITE       

        

         Libevent2.0.1-alpha版本以来,同一时刻,针对同一个文件描述符,可以有任意数量的event在同样的条件上“挂起”。比如,当给定的fd变为可读时,可以使两个events都变为激活状态。但是他们的回调函数的调用顺序是未定义的。

 

         所有这些标志都在<event2/event.h>中定义。

 

二:事件持久性

         默认情况下,当一个“挂起”的event变为“激活”时(要么是因为fd准备好读或写,要么是超时时间到),那么在它的回调函数执行之前,它就会变为“非挂起”状态因此,如果希望再次使event变为“挂起”状态,可以在回调函数内部再次调用event_add函数。

         如果event设置了EV_PERSIST标志,那么event就是“持久”的这意味着event在回调函数激活的时候,依然保持“挂起”状态。如果希望在回调函数中将event变为“非挂起”状态,则可以调用event_del函数。

 

         event的回调函数运行时,“持久”event的超时时间就会被重置。因此,如果某个event标志为EV_READ|EV_PERSIST,并且将超时时间设置为5秒,则该event在下面的条件发生时,会变为“激活”:

当该socket准备好读时;

距离上次event变为激活状态后,又过了5秒钟。

 

三:创建一个可以将自身作为回调函数参数的的event

         经常可能会希望创建这样一个event,它本身就是是回调函数的参数之一。不能仅仅传递一个指向event的指针作为event_new的参数,因为彼时它还没有创建。此时,可以通过调用event_self_cbarg函数解决这样的问题。

void*event_self_cbarg();

         该函数返回一个“魔术”指针,使得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 timerto get 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和evsignal_assign一起使用。然而对于非event来说,他不会作为回调函数的参数。

 

四:纯超时events

         方便起见,Libevent提供了一系列以evtimer_开头的宏,这些宏可以代替event_*函数,来分配和操作纯超时events。使用这些宏仅能提高代码的清晰度而已。

#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))

 

 

五:构造信号事件

         Libevent也可以监控POSIX类的信号。构建一个信号处理函数,可以使用下面的接口:

#define evsignal_new(base,  signum,  cb,  arg)\

    event_new(base,  signum,  EV_SIGNAL|EV_PERSIST,  cb,  arg)

         除了提供一个代表信号值的整数,而不是一个文件描述符之外。它的参数与event_new是一样的。

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);

         注意:信号回调函数是在信号发生之后,在eventloop中调用的。所以,它们可以调用那些,对于普通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))

         警告:当前版本的Libevent,对于大多数的后端方法来说,同一时间,每个进程仅能有一个event_base可以用来监听信号。如果一次向两个event_base添加event,即使是不同的信号,也仅仅会只有一个event_base可以接收到信号。对于kqueue来说,不存在这样的限制。

 

 

六:不在堆中分配event

         出于性能或者其他原因的考虑,一些人喜欢将event作为一个大的结构体的一部分进行分配。对于这样的event,它节省了:

内存分配器在堆上分配小对象的开销;

event指针的解引用的时间开销;

如果event没有在缓存中,缓存不命中的时间开销。

 

         这种方法的风险在于,与其他版本的Libevent之间不满足二进制兼容性,他们可能具有不同的event大小。

 

         这些开销都非常小,对于大多数应用来说是无关紧要的。除非确定知道,应用程序因为使用堆分配的event而存在严重的性能损失,否则应该坚持实用event_new如果后续版本的Libevent使用比当前Libevent更大的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_assign的参数与event_new相同,除了event参数,该参数指针必须指向一个未初始化的event。该函数成功时返回0,失败时返回-1.

#include <event2/event.h>

/*Watch out! Including event_struct.h means that your code willnot

 * be binary-compatible with future versions ofLibevent. */

#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来初始化栈或者静态存储区中的events。

 

警告:

         对于已经在event_base中处于“挂起”状态的event,永远不要调用event_assign这样做会导致极为难以诊断的错误。如果event已经初始化,并且处于“挂起”状态,那么在调用event_assign之前应该先调用event_del。

 

         对于使用event_assign分配的纯超时event或者信号event,同样有方便的宏可以使用:

#define evtimer_assign(event,  base,  callback, arg) \

              event_assign(event, base,  -1,  0,  callback,  arg)

#define evsignal_assign(event,  base,  signum, callback,  arg) \

              event_assign(event, base,  signum,  EV_SIGNAL|EV_PERSIST,  callback,  arg)

        

         如果需要在与未来版本的LIbevent保持二进制兼容性的同时,使用event_assign,可以调用Libevent中的函数,得到运行时的event结构大小:

size_t event_get_struct_event_size(void);

         该函数返回需要为event结构预留的字节数。再次提醒,只有在确定堆分配导致很明显的性能问题时,才应该使用该函数,因为它使你的代码难读又难写

         注意,将来版本的event_get_struct_event_size()的返回值可能比sizeof(structevent)小,这意味着event结构的末尾的额外字节仅仅是保留用于未来版本的Libevent的填充字节。

         下面是一个使用event_get_struct_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 lesserror-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 */

#defineEVENT_PAIR_SIZE() \

            (sizeof(struct  event_pair)+2*event_get_struct_event_size())

 

voidreadcb(evutil_socket_t, short, void *);

voidwritecb(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;

}

         event_assign函数定义在文件<event2/event.h>中。event结构体定义在<event2/event_struct.h>文件中。

 

七:将events置为“挂起”或者“非挂起”

         刚创建的一个event,实际上不能做任何事,直到通过调用event_add进行adding操作,将其置为“挂起”状态。

int  event_add(struct  event *ev,  const  struct timeval  *tv);

         在“非挂起”状态的events上执行event_add操作,则会使得该event在配置的event_base上变为“挂起”状态。该函数返回0表示成功,返回-1表示失败。如果tv为NULL,则该event没有超时时间。否则,tv以秒和毫妙表示超时时间。

         如果在已经是“挂起”状态的event进行event_add操作,则会保持其“挂起”状态,并且会重置其超时时间。如果event已经是“挂起”状态,而且以NULL为超时时间对其进行re-add操作,则event_add没有任何作用。

         注意:不要设置tv为希望超时事件执行的时间,比如如果置tv->tv_sec=time(NULL)+10,并且当前时间为2010/01/01,则超时时间为40年之后,而不是10秒之后。

 

int  event_del(struct event *ev);

         在已经初始化状态的event上调用event_del,则会将其状态变为“非挂起”以及“非激活”状态。如果event的当前状态不是“挂起”或“激活”状态,则该函数没有任何作用。该函数返回0表示成功,返回-1表示失败。

         注意,如果在event刚变为“激活”状态,但是它的回调函数还没有执行时,调用event_del函数,则该操作使得它的回调函数不会执行。

 

int  event_remove_timer(struct  event *ev);

         最后,可以在不删除event上的IO事件或信号事件的情况下,删除一个“挂起”状态的event上的超时事件。如果该event没有超时事件,则event_remove_timer没有作用。如果event没有IO事件或信号事件,只有超时事件的话,则event_remove_timer等同于event_del。该函数返回0表示成功,-1表示失败。

         这些函数都是在文件<event2/event.h>中定义的。

 

八:事件的优先级

         当多个事件在同一时间触发时,Libevent对于他们回调函数的调用顺序是没有定义的。可以通过优先级,定义某些“更重要”的events

         每一个event_base都有一个或多个优先级的值。event初始化之后,添加到event_base之前,可以设置该event的优先级。

int  event_priority_set(struct  event *event,  int  priority);

         event的优先级数必须是位于0到“event_base优先级”-1这个区间内。该函数返回0表示成功,返回-1表示失败。

         当具有多种优先级的多个events同时激活的时候,低优先级的events不会运行。Libevent会只运行高优先级的events,然后重新检查events。只有当没有高优先级的events激活时,才会运行低优先级的events

#include <event2/event.h>

 

void  read_cb(evutil_socket_t,  short,  void*);

void  write_cb(evutil_socket_t,  short,  void*);

 

voidmain_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 longeractive.*/

}

         如果没有设置一个event的优先级,则它的默认优先级是“event_base队列长度”除以2。该函数在文件<event2/event.h>中声明。

 

九:检查event状态

         有时可能希望知道event是否已经添加了(处于“挂起”状态),或者检查他关联到哪个event_base等。

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是否处于“挂起”或“激活”状态。如果确实如此,并且在what参数中设置了任何EV_READ, EV_WRITE, EV_SIGNALEV_TIMEOUT标志的话,则该函数返回所有该event当前正在“挂起”或“激活”的标志。

         如果提供了tv_out参数,且在what参数中设置了EV_TIMEOUT参数,并且当前event确实在超时事件上“挂起”或者“激活”,则tv_out就会设置为event的超时时间。

 

         event_get_fd和event_get_signal函数返回event上配置的文件描述符或者信号值。event_get_base()返回其配置的event_base。event_get_events() 返回event上配置的事件标志(EV_READ,EV_WRITE等)。event_get_callback函数和event_get_callback_arg函数返回event的回调函数和参数指针。event_get_priority函数返回event的当前优先级。

 

         event_get_assignment函数在提供的参数指针中返回event的所有成分,如果参数指针为NULL,则该成分被忽略。

#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 notre-assign a

         * pending event.  That would be very very bad.*/

       fprintf(stderr, "Error! replace_callbackcalled on a pending event!\n");

        return -1;

    }

 

    event_get_assignment(ev,  &base, &fd,  &events,

                         NULL /* ignore oldcallback */ ,

                         NULL /* ignore oldcallback argument */);

 

   event_assign(ev,  base,  fd,  events,new_callback,  new_callback_arg);

    return 0;

}

         这些函数在文件<event2/event.h>中定义。

 

十:找到当前正在运行的event

         在调试程序时,可以得到当前正在运行的event的指针。

struct event * event_base_get_running_event(struct event_base * base);

         注意,只有在base的loop中调用该函数,该函数才有意义。在其他线程调用时不支持的,而且会导致未定义的行为。

         该函数在<event2/event.h>中声明。

 

十一:配置一次性的events    

         如果不需要对一个event进行多次添加,或者对一个非持久的event,在add之后就会delete,则可以使用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_SIGNALEV_PERSIST标志。得到的内部event会以默认的优先级添加到event_base中并运行。当它的回调函数执行完成之后,Libevent将会释放该内部event。该函数成功时返回0,失败是返回-1.

 

         通过event_base_once插入的event不能被删除或者手动激活。如果希望可以取消一个event,则需要通过常规的event_new或event_assign接口创建event。

        

         注意,直到Libevent2.0之前,如果event一直没有触发,则它的内存永远不会被释放。从Libevent2.1.2-alpha版本开始,当event_base释放时,即使events还没有被激活,它们的内存也会被释放。但是依然要注意:如果它们的回调函数的参数具有关联的内存,那么除非程序中进行释放,否则这些内存永远不会被释放。

 

十二:手动激活event

         某些极少的情况下,你可能希望在条件未被触发的情况下就激活event;

void  event_active(struct  event *ev, int  what, short  ncalls);

         该接口使得event变为“激活”状态,激活标志在what中传入(EV_READ, EV_WRITEEV_TIMEOUT的组合)。该event之前的状态不一定非得要是“挂起”状态,而且将其激活不会使其状态变为“挂起”状态。

         警告:在同一个event上递归调用event_active可能会导致资源耗尽。下面的例子就是不正确的示范:

structevent *ev;

 

static void cb(int  sock,  short which,  void *arg) {

        /* Whoops: Calling event_active on thesame event unconditionally

          from within its callback means that no other eventsmight not get

           run!*/

        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;

}

         上面的例子描述了这样的情形:event loop仅被执行一次,而cb会被无限的递归调用中。

Example:Alternative solution to the above problem using timers

 

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;

}

 

Example:Alternative solution to the above problem usingevent_config_set_max_dispatch_interval()

 

structevent *ev;

 

static void  cb(int  sock, short  which,  void*arg) {

        event_active(ev,  EV_WRITE,  0);

}

 

intmain(int argc,  char **argv) {

        struct  event_config  *cfg = event_config_new();

        /* Run at most 16 callbacks beforechecking 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>中定义。

 

十三:优化一般性超时

         当前版本的Libevent使用二叉堆算法来对”挂起”状态的event超时时间值进行跟踪。对于有序的添加和删除event超时时间的操作二叉堆算法可以提供O(lg n)的性能。这对于添加随机分布的超时时间来说,性能是最优的,但是如果是大量相同时间的events来说就不是了。

         比如,假设有一万个事件,每一个event的超时时间都是在他们被添加之后的5秒钟。在这种情况下,使用双向队列实现的话,可以达到O(1)的性能。

 

         正常情况下,一般不希望使用队列管理所有的超时时间值,因为队列仅对于恒定的超时时间来说是快速的。如果一些超时时间或多或少的随机分布的话,那添加这些超时时间到队列将会花费O(n)的时间,这样的性能要比二叉堆差多了。

         Libevent解决这种问题的方法是将一些超时时间值放置在队列中,其他的则放入二叉堆中。可以向Libevent请求一个“公用超时时间”的时间值,然后使用该时间值进行事件的添加。如果存在大量的event,它们的超时时间都是这种单一公用超时时间的情况,那么使用这种优化的方法可以明显提高超时事件的性能。

const struct  timeval * event_base_init_common_timeout(

     struct event_base *base,  const  struct  timeval* duration);

         该方法的参数有event_base,以及一个用来初始化的公用超时时间值。该函数返回一个指向特殊timeval结构体的指针,可以使用该指针表明将event添加到O(1)的队列中,而不是O(lg n)的堆中。这个特殊的timeval结构可以在代码中自由的复制和分配。该timeval只能工作在特定的event_base上(参数)。不要依赖于该timeval的实际值:Libevent仅使用它们来指明使用哪个队列。

#include <event2/event.h>

#include <string.h>

 

/*We're going to create a verylarge number of events on a given base,

 * nearly all of which have a ten-secondtimeout. If initialize_timeout

 * is called, we'll tell Libevent to add theten-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 sameevent_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);

}

         类似于其他所有的优化函数,除非确定对你有用,否则应该避免使用这种公用超时时间功能。

 

十四:从已清除的内存识别事件

         Libevent提供了这样的函数,可以从已经清0的内存中(比如以calloc分配,或者通过memset或bzero清除)识别出已初始化的event

int  event_initialized(const  struct  event*ev);

 

#define evsignal_initialized(ev)  event_initialized(ev)

#define evtimer_initialized(ev)  event_initialized(ev)

         警告:这些函数不能在一块未初始化的内存中识别出已经初始化了的event。除非你能确定该内存要么被清0,要么被初始化为event,否则不要使用这些函数。

         一般情况下,除非你的应用程序有着极为特殊的需求,否则不要轻易使用这些函数。通过event_new返回的events永远已经初始化过的。

#include <event2/event.h>

#include <stdlib.h>

 

struct reader {

    evutil_socket_t  fd;

};

 

#defineREADER_ACTUAL_SIZE()  (sizeof(struct  reader) + event_get_struct_event_size())

#defineREADER_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处理函数

         在Libevent2.0之前的版本中,没有event_assign或者event_new函数,而只有event_set函数,该函数返回的event与“当前”base相关联。如果有多个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_set函数类似于event_assign,除了它使用“当前”base的概念。event_base_set函数改变event所关联的base。

         如果是处理超时或者信号events,event_set也有一些便于使用的变种:evtimer_set类似于evtimer_assign,而evsignal_set类似于evsignal_assign。

 

         Libevent2.0之前的版本中,使用以signal_为前缀的函数作为处理基于信号的event_set的变种。而不是evsignal_(也就是说是:signal_set, signal_add, signal_del, signal_pending和signal_intialized)。Libevent古老版本(0.6之前),使用timeout_,而不是evtimer_前缀。因此,如果你需要处理很老的代码的话,可能会看见timeout_add(), timeout_del(), timeout_initialized(),  timeout_set(),  timeout_pending()等。

 

         较老版本的Libevent(2.0之前)使用两个宏,完成event_get_fd和event_get_signal的工作:EVENT_FD和EVENT_SIGNAL。这些宏直接监测event结构的内部,因此在各种版本之间不具有二进制兼容性。在2.0以及之后的版本中,这些宏就是event_get_fd和event_get_signal函数的别名。

 

         在Libevent2.0之前的版本中不支持锁操作,因此,在运行base的线程之外的线程中,调用任何改变event状态的函数,都是不安全的。这些函数包括:event_add, event_del, event_active, and event_base_once。

 

         event_base_once的古老版本是event_once,它使用“当前”base的概念。

         在Libevent2.0之前,超时事件设置EV_PERSISIT是不明智的。并非在event激活时重置超时时间,EV_PERSISIT标志对于超时而言无任何作用。

         Libevent2.0之前的版本不支持多个events,在同一时间添加同样的fd以及相同的READ/WRITE事件。换句话说,对于每一个fd,一次只能有一个event在其上等待读或写。

 

 

http://www.wangafu.net/~nickm/libevent-book/Ref4_event.html

你可能感兴趣的:(Libevent:5events相关)