1. 程式人生 > >讓libevent支援多執行緒

讓libevent支援多執行緒

libevent的使用方式是最開始呼叫event_init初始化一個全域性的event_base指標,以後使用其中的API新增新的事件均是對這個指標進行的操作.

試想如下一種典型的場景:主執行緒使用libevent處理網路IO事件,接收新連線以及接收完客戶端的資料之後將該事件交給輔助執行緒進行處理,輔助執行緒處理完了,需要往客戶端傳送迴應資料,則再通過libevent提供的API將這個事件新增到可讀事件中.但是,由於libevent中這個event_base指標是全域性的,如果多執行緒同時新增可能會造成多執行緒不安全問題,這個在libevent的程式碼註釋裡面作者也做了註解.

在這個地方有人給出瞭解決的方案:
已經有現成的解決方案:
http://monkeymail.org/archives/libevent-users/2006-October/000257.html

原作者給的地址好像已經失效了。
iunknown
spserver 中就是使用了他的程式碼,可以從這裡獲得
http://code.google.com/p/spserver/source/browse/trunk/spserver/event_msgqueue.h
(對應的C檔案可以通過修改上面檔案的字尾名所得).

簡單的說一說這個event_msgqueue的原理及使用方式:
它的結構體為:
struct event_msgqueue {
   
int push_fd;
   
int pop_fd;
   
int unlock_between_callbacks;

   
structevent queue_ev;

   sp_thread_mutex_t 
lock;
   
void (*callback)(void*void*);
   
void*cbarg;
   
struct circqueue *queue;
};
其中的queue是儲存事件的佇列,callback是處理事件的回撥函式, lock是執行緒鎖.

首先,它建立了一對socketpair(也就是結構體中的push_fd和pop_fd),將其中的一個通過libevent的event_add介面新增到關注的事件中,它的事件型別的是READ|PERSIST, 也就是說可讀同時永不刪除,而這個事件的回撥函式是msgqueue_pop,這個函式的功能是將當前queue中的資料一一取出並且呼叫callback函式進行回撥處理.
其次,外部的多執行緒需要往libevent新增事件時使用這個檔案提供的msgqueue_push函式進行新增,此時, 往socketpair中傳送一個位元組的資料, 這麼做的目的是為了讓第一步已經新增的socketpair得到響應,此時, 由第一步,必然會觸發之前註冊的回撥函式msgqueue_pop,將這個佇列中的事件取出來進行處理.

這裡使用socketpair起到了一個通知的作用,當佇列中有資料時,通過向socketpair傳送資料以呼叫msgqueue_pop,將資料取出來進行處理.
可見,這個event_msgqueue起了一箇中間層的作用,輔助執行緒要往libevent新增事件就通過這個佇列,當佇列中有資料時再觸發libevent的事件處理機制進行處理.設計的非常巧妙.

感謝
iunknown
兄的指點:)