libevent
源碼深度剖析十三
——
libevent
訊號處理注意點
前面講到了
libevent
實現多線程的方法,然而在多線程的環境中註冊訊號事件,還是有一些情況需要小心處理,那就是不能在多個
libevent
執行個體上註冊訊號事件。依然冠名追加到
libevent
系列。
以
2
個線程為例,做簡單的情境分析。
1
首先是建立並初始化線程
1
的
libevent
執行個體
base1
,線程
1
的
libevent
執行個體
base2
;
2
在
base1
上註冊
SIGALRM
訊號;在
base2
上註冊
SIGINT
訊號;
3
假設當前
base1
和
base2
上都沒有註冊其他的事件;
4
線程
1
和
2
都進入
event_base_loop
事件迴圈:
event_base_loop(base1)
event_base_loop(base2)
{
{
if (base2->sig.ev_signal_added)
if (base2->sig.ev_signal_added)
evsignal_base = base1;
evsignal_base = base2;
while(!done)
while(!done)
{
{
…
…
evsel->dispatch(…);
evsel->dispatch(…);
…
…
}
}
}
}
5
假設線程
1
先進入
event_base_loop
,並設定
evsignal_base = base1
;並等待;
6
接著線程
2
也進入
event_base_loop
,並設定
evsignal_base = base2
;並等待;
於是
evsignal_base
就指向了
base2
;
7
訊號
ALARM
觸發,調用服務常式:
static void evsignal_handler(int sig)
{
...
evsignal_base->sig.evsigcaught[sig]++;
evsignal_base->sig.evsignal_caught
= 1;
/*
Wake up our notification mechanism */
send(evsignal_base->sig.ev_signal_pair[0],
"a", 1, 0);
...
}
於是
base2
得到通知
ALARM
訊號發生了,而實際上
ALARM
是註冊在
base1
上的,
base2
上的
ALARM
註冊
event
是空的,於是處理函數將不能得到調用;
因此在
libevent
中,如果需要處理訊號,只能將訊號註冊到一個
libevent
執行個體上。
memcached
就沒有使用
libevent
提供的
signal
介面,而是直接使用系統提供的原生
API
,看起來這樣更簡潔。