在編寫多線程程序的時候我們經常需要判斷臨界條件,如對象池中的可用數,某一個對象是否可用等狀態.當我們這麼做的時候其實就已經錯了,為什麼這麼說因為在我們的潛意識裡一直在使用絕對時間點來判定多線程程序.因為我們習慣性的用鎖然後判定當前時間點的臨界條件狀態並作出相應處理.這樣做當然可以但是在理念上錯了,鎖是為了同步資源用的而不是臨界條件(當然你們做也沒關系不過我向往無鎖並發).那我們不依賴絕對時間點還能依賴什麼呢?那就是事件,不過這個event可不是.net裡的那個關鍵字.這是個邏輯上的概念.所謂多線程中的事件是指一定會發生的事情.比如要使用對象池中對象,這就是一個event.那麼就會發生而不是依靠判斷當前可用數,因為你讀取的那一刻可以使用可是調用的時候可能就沒有可用的了(當然可以用鎖,不過鎖的效率實在很低還會死鎖).那我們到底怎麼處理這個事件呢,在絕大多數情況下使用semaphore(有時可以用InterLocked請看我的"自己實現一個Semaphore "裡對2個臨界條件的處理).說到這就簡單了,我們把對具體條件的判斷更改為對semaphore對象的wait.這樣就不會出現當臨界條件不滿足時用while+sleep進行處理的丑陋代碼了.
在我的編程體系裡我給semaphore起了個別名叫做Event_Trigger 事件觸發器.它的目的就是用來控制一個事件是否可以出發的.比如在多線程編程中生產消費者模型是一個最典型,最常用的模型.所以我們一般都會實現一個生產消費隊列.這個queue很簡單就是生產者把生產出來的產片放到隊列中,而消費者從隊列中拿走消費.在日常生活中最直觀的例子就是蛋糕店,糕點師把做好的蛋糕放到櫃台上供大家購買.這裡櫃台就是這個queue而這就有一個問題了櫃台放滿了怎麼辦?我們可能會說queue嘛可以動態增長的,但是內存也是有限的我們不能讓queue無限增長下去.所以我們需要二個臨界條件:最大queue數和是否可消費來進行控制.但實際上我們要的是二個事件,生產事件和消費事件,如果事件不能發生就阻塞如此而已.
下面我們來看看怎麼利用事件的機制來編寫這個queue,我個人認為比判斷絕對時間點的方式更好,更直接(因為這個隊列非常簡單我只給出關鍵的代碼片斷).
Event_Trigger produce; //最小可用0,最大數為int.max
Event_Trigger _consume; //同上
public void Push(Item i) //生產
{
_produce.Wait(); //等待生產事件發生
queue.Enqueue(i);
_consume.Post(); //通知消費事件發生
}
Pop方法中使用_consume.Wait(),_produce.Post().
這樣是不是很簡潔,很直觀.我當時在傾聽了我boss的講解以後真是有醍醐灌頂的感覺,上邊寫的一堆可能沒有表述清楚,不過我強烈想大家推薦這種使用事件代替臨界條件判斷的方式.