Libevent參考手冊第一章:設置libevent(二)

4 鎖和線程windows

編寫多線程程序的時候,在多個線程中同時訪問一樣的數據並不老是安全的。api

libevent的結構體在多線程下一般有三種工做方式:安全

²  某些結構體內在地是單線程的:同時在多個線程中使用它們老是不安全的。多線程

²  某些結構體具備可選的鎖:能夠告知libevent是否須要在多個線程中使用每一個對象。socket

²  某些結構體老是鎖定的:若是libevent在支持鎖的配置下運行,在多個線程中使用它們老是安全的。ide

爲獲取鎖,在調用分配須要在多個線程間共享的結構體的libevent函數以前,必須告知libevent使用哪一個鎖函數。函數

若是使用pthreads庫,或者使用Windows本地線程代碼,那麼你是幸運的:已經有設置好的libevent預約義函數可以正確的使用pthreads或者Windows函數。oop

接口

  1. #ifdef WIN32  
  2. int evthread_use_windows_threads(void);  
  3. #define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED  
  4. #endif  
  5. #ifdef _EVENT_HAVE_PTHREADS  
  6. int evthread_use_pthreads(void);  
  7. #define EVTHREAD_USE_PTHREADS_IMPLEMENTED  
  8. #endif  

 

這些函數在成功時都返回0,失敗時返回-1。ui

若是使用不一樣的線程庫,則須要一些額外的工做,必須使用你的線程庫來定義函數去實現:spa

l  鎖

l  鎖定

l  解鎖

l  分配鎖

l  析構鎖

l  條件變量

l  建立條件變量

l  析構條件變量

l  等待條件變量

l  觸發/廣播某條件變量

l  線程

l  線程ID檢測

使用evthread_set_lock_callbacks和evthread_set_id_callback接口告知libevent這些函數。

接口

 

  1. #define EVTHREAD_WRITE  0x04  
  2. #define EVTHREAD_READ   0x08  
  3. #define EVTHREAD_TRY    0x10  
  4.   
  5. #define EVTHREAD_LOCKTYPE_RECURSIVE 1  
  6. #define EVTHREAD_LOCKTYPE_READWRITE 2  
  7.   
  8. #define EVTHREAD_LOCK_API_VERSION 1  
  9.   
  10. struct evthread_lock_callbacks {  
  11.        int lock_api_version;  
  12.        unsigned supported_locktypes;  
  13.        void *(*alloc)(unsigned locktype);  
  14.        void (*free)(void *lock, unsigned locktype);  
  15.        int (*lock)(unsigned mode, void *lock);  
  16.        int (*unlock)(unsigned mode, void *lock);  
  17. };  
  18.   
  19. int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *);  
  20.   
  21. void evthread_set_id_callback(unsigned long (*id_fn)(void));  
  22.   
  23. struct evthread_condition_callbacks {  
  24.         int condition_api_version;  
  25.         void *(*alloc_condition)(unsigned condtype);  
  26.         void (*free_condition)(void *cond);  
  27.         int (*signal_condition)(void *cond, int broadcast);  
  28.         int (*wait_condition)(void *cond, void *lock,  
  29.             const struct timeval *timeout);  
  30. };  
  31.   
  32. int evthread_set_condition_callbacks(  
  33.         const struct evthread_condition_callbacks *);  

 evthread_lock_callbacks結構體描述的鎖回調函數及其能力。對於上述版本,lock_api_version字段必須設置爲EVTHREAD_LOCK_API_VERSION。必須設置supported_locktypes字段爲EVTHREAD_LOCKTYPE_*常量的組合以描述支持的鎖類型(在2.0.4-alpha版本中,EVTHREAD_LOCK_RECURSIVE是必須的,EVTHREAD_LOCK_READWRITE則沒有使用)。alloc函數必須返回指定類型的新鎖;

free函數必須釋放指定類型鎖持有的全部資源;lock函數必須試圖以指定模式請求鎖定,若是成功則返回0,失敗則返回非零;unlock函數必須試圖解鎖,成功則返回0,不然返回非零。

可識別的鎖類型有:

0:一般的,沒必要遞歸的鎖。

EVTHREAD_LOCKTYPE_RECURSIVE:不會阻塞已經持有它的線程的鎖。一旦持有它的線程進行原來鎖定次數的解鎖,其餘線程馬上就能夠請求它了。

EVTHREAD_LOCKTYPE_READWRITE:可讓多個線程同時由於讀而持有它,可是任什麼時候刻只有一個線程由於寫而持有它。寫操做排斥全部讀操做。

可識別的鎖模式有:

EVTHREAD_READ:僅用於讀寫鎖:爲讀操做請求或者釋放鎖

EVTHREAD_WRITE:僅用於讀寫鎖:爲寫操做請求或者釋放鎖

EVTHREAD_TRY:僅用於鎖定:僅在能夠馬上鎖定的時候才請求鎖定

id_fn參數必須是一個函數,它返回一個無符號長整數,標識調用此函數的線程。對於相同線程,這個函數應該老是返回一樣的值;而對於同時調用該函數的不一樣線程,必須返回不一樣的值。

evthread_condition_callbacks結構體描述了與條件變量相關的回調函數。對於上述版本,condition_api_version字段必須設置爲EVTHREAD_CONDITION_API_VERSION。alloc_condition函數必須返回到新條件變量的指針。它接受0做爲其參數。free_condition函數必須釋放條件變量持有的存儲器和資源。wait_condition函數要求三個參數:一個由alloc_condition分配的條件變量,一個由你提供的evthread_lock_callbacks.alloc函數分配的鎖,以及一個可選的超時值。調用本函數時,必須已經持有參數指定的鎖;本函數應該釋放指定的鎖,等待條件變量成爲授信狀態,或者直到指定的超時時間已經流逝(可選)。wait_condition應該在錯誤時返回-1,條件變量授信時返回0,超時時返回1。返回以前,函數應該肯定其再次持有鎖。最後,signal_condition函數應該喚醒等待該條件變量的某個線程(broadcast參數爲false時),或者喚醒等待條件變量的全部線程(broadcast參數爲true時)。只有在持有與條件變量相關的鎖的時候,纔可以進行這些操做。

關於條件變量的更多信息,請查看pthreads的pthread_cond_*函數文檔,或者Windows的CONDITION_VARIABLE(Windows Vista新引入的)函數文檔。

示例

關於使用這些函數的示例,請查看Libevent源代碼發佈版本中的evthread_pthread.c和evthread_win32.c文件。

這些函數在<event2/thread.h>中聲明,其中大多數在2.0.4-alpha版本中首次出現。2.0.1-alpha到2.0.3-alpha使用較老版本的鎖函數。event_use_pthreads函數要求程序連接到event_pthreads庫。

條件變量函數是2.0.7-rc版本新引入的,用於解決某些棘手的死鎖問題。

能夠建立禁止鎖支持的libevent。這時候已建立的使用上述線程相關函數的程序將不能運行。

5 調試鎖的使用

爲幫助調試鎖的使用,libevent有一個可選的「鎖調試」特徵。這個特徵包裝了鎖調用,以便捕獲典型的鎖錯誤,包括:

l  解並無真正持有的鎖。

l  從新鎖定一個非遞歸鎖。

若是發生這些錯誤中的某一個,libevent將給出斷言失敗而且退出。

接口

void evthread_enable_lock_debuging(void);

 

注意

必須在建立或者使用任何鎖以前調用這個函數。爲安全起見,請在設置完線程函數後當即調用這個函數。

這個函數是在2.0.4-alpha版本新引入的。

6 調試事件的使用

libevent能夠檢測使用事件時的一些常見錯誤而且進行報告。這些錯誤包括:

l  將未初始化的event結構體看成已經初始化的。

l  試圖從新初始化一個掛起的event結構體。

跟蹤哪些事件已經初始化須要使用額外的內存和處理器時間,因此只應該在真正調試程序的時候才啓用調試模式。

接口

void event_enable_debug_mode(void);
 

必須在建立任何event_base以前調用這個函數。

若是在調試模式下使用大量由event_assign()(而不是event_new())建立的事件,程序可能會耗盡內存,這是由於沒有方式能夠告知libevent由event_assign()建立的事件不會再被使用了(能夠調用event_free()告知由event_new()建立的事件已經無效了)。若是想在調試時避免耗盡內存,能夠顯式告知libevent由event_assign()建立的這些事件再也不被看成已分配的了:

接口

void event_debug_unassign(struct event *ev);

 

沒有啓用調試的時候調用event_debug_unassign沒有效果。

示例

  1. #include <event2/event.h>  
  2. #include <event2/event_struct.h>  
  3.   
  4. #include <stdlib.h>  
  5.   
  6. void cb(evutil_socket_t fd, short what, void *ptr)  
  7. {  
  8.     /* We pass 'NULL' as the callback pointer for the heap allocated 
  9.      * event, and we pass the event itself as the callback pointer 
  10.      * for the stack-allocated event. */  
  11.     struct event *ev = ptr;  
  12.   
  13.     if (ev)  
  14.         event_debug_unassign(ev);  
  15. }  
  16.   
  17. /* Here's a simple mainloop that waits until fd1 and fd2 are both 
  18.  * ready to read. */  
  19. void mainloop(evutil_socket_t fd1, evutil_socket_t fd2, int debug_mode)  
  20. {  
  21.     struct event_base *base;  
  22.     struct event event_on_stack, *event_on_heap;  
  23.   
  24.     if (debug_mode)  
  25.        event_enable_debug_mode();  
  26.   
  27.     base = event_base_new();  
  28.   
  29.     event_on_heap = event_new(base, fd1, EV_READ, cb, NULL);  
  30.     event_assign(&event_on_stack, base, fd2, EV_READ, cb, &event_on_stack);  
  31.   
  32.     event_add(event_on_heap, NULL);  
  33.     event_add(&event_on_stack, NULL);  
  34.   
  35.     event_base_dispatch(base);  
  36.   
  37.     event_free(event_on_heap);  
  38.     event_base_free(base);  
  39. }  

 

這些調試函數在libevent 2.0.4-alpha版本中加入。

 

 

7 檢測libevent的版本

新版本的libevent會添加特徵,移除bug。有時候須要檢測libevent的版本,以便:

l  檢測已安裝的libevent版本是否可用於建立你的程序。

l  爲調試顯示libevent的版本。

l  檢測libevent的版本,以便向用戶警告bug,或者提示要作的工做。

接口

 

[c-sharp] view plain copy
  1. #define LIBEVENT_VERSION_NUMBER 0x02000300  
  2. #define LIBEVENT_VERSION "2.0.3-alpha"  
  3. const char *event_get_version(void);  
  4. ev_uint32_t event_get_version_number(void);  

 

宏返回編譯時的libevent版本;函數返回運行時的libevent版本。注意:若是動態連接到libevent,這兩個版本可能不一樣。

能夠獲取兩種格式的libevent版本:用於顯示給用戶的字符串版本,或者用於數值比較的4字節整數版本。整數格式使用高字節表示主版本,低字節表示副版本,第三字節表示修正版本,最低字節表示發佈狀態:0表示發佈,非零表示某特定發佈版本的後續開發序列。

因此,libevent 2.0.1-alpha發佈版本的版本號是[02 00 01 00],或者說0x02000100。2.0.1-alpha和2.0.2-alpha之間的開發版本多是[02 00 01 08],或者說0x02000108。

示例:編譯時檢測

 

  1. #include <event2/event.h>  
  2.   
  3. #if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000100  
  4. #error "This version of Libevent is not supported; Get 2.0.1-alpha or later."  
  5. #endif  
  6.   
  7. int  
  8. make_sandwich(void)  
  9. {  
  10.         /* Let's suppose that Libevent 6.0.5 introduces a make-me-a 
  11.            sandwich function. */  
  12. #if LIBEVENT_VERSION_NUMBER >= 0x06000500  
  13.         evutil_make_me_a_sandwich();  
  14.         return 0;  
  15. #else  
  16.         return -1;  
  17. #endif  
  18. }  

 

 

示例:運行時檢測

  1. #include <event2/event.h>  
  2. #include <string.h>  
  3.   
  4. int  
  5. check_for_old_version(void)  
  6. {  
  7.     const char *v = event_get_version();  
  8.     /* This is a dumb way to do it, but it is the only thing that works 
  9.        before Libevent 2.0. */  
  10.     if (!strncmp(v, "0.", 2) ||  
  11.         !strncmp(v, "1.1", 3) ||  
  12.         !strncmp(v, "1.2", 3) ||  
  13.         !strncmp(v, "1.3", 3)) {  
  14.   
  15.         printf("Your version of Libevent is very old.  If you run into bugs,"  
  16.                " consider upgrading./n");  
  17.         return -1;  
  18.     } else {  
  19.         printf("Running with Libevent version %s/n", v);  
  20.         return 0;  
  21.     }  
  22. }  
  23.   
  24. int  
  25. check_version_match(void)  
  26. {  
  27.     ev_uint32_t v_compile, v_run;  
  28.     v_compile = LIBEVENT_VERSION_NUMBER;  
  29.     v_run = event_get_version_number();  
  30.     if ((v_compile & 0xffff0000) != (v_run & 0xffff0000)) {  
  31.         printf("Running with a Libevent version (%s) very different from the "  
  32.                "one we were built with (%s)./n", event_get_version(),  
  33.                LIBEVENT_VERSION);  
  34.         return -1;  
  35.     }  
  36.     return 0;  
  37. }  

 

本節描述的宏和函數定義在<event2/event.h>中。event_get_version函數首次出如今1.0c版本;其餘的首次出如今2.0.1-alpha版本。

相關文章
相關標籤/搜索