libevent的使用方式是最開始調(diào)用event_init初始化一個全局的event_base指針,以后使用其中的API添加新的事件均是對這個指針進(jìn)行的操作.
試想如下一種典型的場景:主線程使用libevent處理網(wǎng)絡(luò)IO事件,接收新連接以及接收完客戶端的數(shù)據(jù)之后將該事件交給輔助線程進(jìn)行處理,輔助線程處理完了,需要往客戶端發(fā)送回應(yīng)數(shù)據(jù),則再通過libevent提供的API將這個事件添加到可讀事件中.但是,由于libevent中這個event_base指針是全局的,如果多線程同時添加可能會造成多線程不安全問題,這個在libevent的代碼注釋里面作者也做了注解.
在這個地方有人給出了解決的方案:
已經(jīng)有現(xiàn)成的解決方案:
http://monkeymail.org/archives/libevent-users/2006-October/000257.html
原作者給的地址好像已經(jīng)失效了。
iunknown在
spserver 中就是使用了他的代碼,可以從這里獲得
http://code.google.com/p/spserver/source/browse/trunk/spserver/event_msgqueue.h(對應(yīng)的C文件可以通過修改上面文件的后綴名所得).
簡單的說一說這個event_msgqueue的原理及使用方式:
它的結(jié)構(gòu)體為:
struct event_msgqueue {
int push_fd;
int pop_fd;
int unlock_between_callbacks;
struct event queue_ev;
sp_thread_mutex_t lock;
void (*callback)(void *, void *);
void *cbarg;
struct circqueue *queue;
};
其中的queue是保存事件的隊列,callback是處理事件的回調(diào)函數(shù), lock是線程鎖.
首先,它創(chuàng)建了一對socketpair(也就是結(jié)構(gòu)體中的push_fd和pop_fd),將其中的一個通過libevent的event_add接口添加到關(guān)注的事件中,它的事件類型的是READ|PERSIST, 也就是說可讀同時永不刪除,而這個事件的回調(diào)函數(shù)是msgqueue_pop,這個函數(shù)的功能是將當(dāng)前queue中的數(shù)據(jù)一一取出并且調(diào)用callback函數(shù)進(jìn)行回調(diào)處理.
其次,外部的多線程需要往libevent添加事件時使用這個文件提供的msgqueue_push函數(shù)進(jìn)行添加,此時, 往socketpair中發(fā)送一個字節(jié)的數(shù)據(jù), 這么做的目的是為了讓第一步已經(jīng)添加的socketpair得到響應(yīng),此時, 由第一步,必然會觸發(fā)之前注冊的回調(diào)函數(shù)msgqueue_pop,將這個隊列中的事件取出來進(jìn)行處理.
這里使用socketpair起到了一個通知的作用,當(dāng)隊列中有數(shù)據(jù)時,通過向socketpair發(fā)送數(shù)據(jù)以調(diào)用msgqueue_pop,將數(shù)據(jù)取出來進(jìn)行處理.
可見,這個event_msgqueue起了一個中間層的作用,輔助線程要往libevent添加事件就通過這個隊列,當(dāng)隊列中有數(shù)據(jù)時再觸發(fā)libevent的事件處理機(jī)制進(jìn)行處理.設(shè)計的非常巧妙.
感謝
iunknown兄的指點(diǎn):)