• 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_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! ");

            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

  • 相关阅读:
    软件测试(理论基础)
    Android NDK常见配置问题的解决方案
    Eclemma各种安装方式以及安装失败解决
    检测Buffer Overflow的几种方法
    转: 跟我一起写 Makefile
    流敏感、路径敏感、上下文敏感
    Symbolic Exectuion with Mixed ConcreteSymbolic Solving
    基于ajc的代码编织
    第一次个人编程作业
    第一次博客作业
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247258.html
Copyright © 2020-2023  润新知