Libevent:5events相關

         Libevents的基本操做單元是event,每個event表明了一些條件的集合,這些條件包括:html

文件描述符已經準備好讀或寫算法

文件描述符正在變爲就緒,準備好讀或寫(僅限於邊沿觸發)後端

超時事件緩存

信號發生安全

用戶觸發事件app

 

         events都有相似的生命週期。一旦調用Libevent函數建立好event,並將其關聯到一個event_base以後,他就是「已初始化」狀態(initialized)。這種狀態下,能夠進行add操做,將其狀態變爲base中的「掛起」狀態(pending),處於「掛起」狀態的event,若是觸發事件的條件發生了(好比,文件描述符的狀態發生變化,或者超時了),那麼event的狀態變爲「激活」狀態(active),而後它的回調函數(用戶提供)開始運行。若是該event配置了「持久」屬性(persistent),那麼它的狀態依然保持爲「掛起」,不然,在回調函數運行時,它的狀態就再也不是「掛起」(「非掛起」狀態)。能夠經過delete操做,將一個「掛起」狀態的event變爲「非掛起」狀態(non-pending),或者經過add操做,將「非掛起」的event變爲「掛起」狀態。less

 

一:構建event對象socket

         建立新的event,可使用event_new接口:函數

#define EV_TIMEOUT      0x01oop

#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

相關文章
相關標籤/搜索