基於消息分發的多線程程序設計,常見的問題,以及解決方法

http://blog.csdn.net/dengyunze/archive/2005/12/16/554007.aspx

<script type="text/javascript">function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>

商業軟件的開發,大部分都需要有一些爲其它模塊提供服務的底層模塊。這些底層模塊由於實現的是一些通用功能,需要同時爲幾個高層模塊提供功能,因此通常被設計成一種基於消息隊列的框架。任何需要訪問這些通用功能的高層模塊,都可以通過發送消息並接受返回值來得到需要的服務。

這種構架的設計,一般是圍繞消息隊列來展開的:首先有一個消息隊列,並對外暴露發送消息的API;然後實現一個負責維護並調度該消息隊列的線程,該線程負責維護消息隊列,並分發消息;最後是一系列處理特定消息的功能模塊。消息由暴露給外層模塊的API發送到消息隊列,由調度線程接受消息,並分發給消息處理模塊,然後由處理模塊對不同消息進行處理,將處理結果返回給高層模塊,這就是一個完善的基於消息隊列的公用模塊。爲了實現這種模塊的可擴展性,消息處理模塊一般採取一種基於註冊的設計,允許用戶註冊特定消息的消息處理函數。

這種構架從結構上看,是非常完善的,模塊功能也比較好劃分,因此應用廣泛。但是根據個人經驗,大部分這種模塊的實現,在細節上都會有一些細微的瑕疵,給後期代碼的維護和擴展帶來麻煩,一下是個人遇到過的3中情況。

1. 消息的定義沒有做到“原子化”。單一的功能被認爲拆分成好幾個消息。比如在一個爲其它模塊提供數據庫訪問服務的模塊,將打開記錄集,獲取記錄集,關閉記錄集分成了3個消息,由於這3個消息在邏輯上有着順序關係,而消息處理並不能很好的保證這種順序關係,從而導致獲取記錄集時,記錄集已經被關閉等等類似情況的出現(由於記錄集可能很大,需要分多次獲取,這種設計有時候也是不可避免)。

要處理這種情況,唯一的辦法是保證想辦法保證消息處理的順序性,比如使用同步消息。由客戶端確保上一條消息已經被處理,結果已經被返回的情況下才發送第二條消息。

2. 主線程接受到退出消息,開始清理內存資源,而消息處理線程仍然在處理消息。這種情況的發生一般會導致軟件報非法內存訪問錯誤,而且一般是crash問題。

處理這種情況,必須確保主線程wait所有其他線程已經退出後,纔開始做清理工作。要做到這一點有時候相當困難。個人遇到過的一種情況是,消息處理線程由公司其它team的人開發,他們在消息隊列創建的時候創建一個線程池,該線程池自動調用我傳遞過去的一個回調函數處理消息。而且線程池的銷燬工作也在該模塊中實現,其結果是在我的模塊中,收到系統退出消息以後,無法準確探測是否所有消息處理線程已經全部中止,是否已經可以開始做資源清理工作。

實現基於消息隊列的架構時,必須在設計初期處理好多個線程的調度關係,以及邏輯順序。

3. 如果基於消息隊列的模塊需要訪問數據庫,那麼還有一種更爲討厭的情況。在某些數據庫系統中(MS SQL Server),記錄集與事務處理之間的嵌套會導致記錄集被破壞。考慮一種情況,高層模塊有如下需求:

a.) 查詢一個數據表。b.) 刪除另外一個表中的2條記錄。

第一個需求可以查分成3個消息:打開表,獲取記錄集,關閉表,由於不改變數據庫,不必使用事務。

第二個需求可以簡單打包成一個消息,由於更改數據庫,需要使用事務機制。

由於消息處理不能保證順序性,如果以上4條消息處理順序如下:

打開表1---- 刪除表2的兩條記錄----獲取表1記錄集中的記錄----關閉表1

第一步操作和第二步操作沒有問題,但是第三步操作,獲取表1記錄集時,某些數據庫將報錯。因爲第二步操作使用了事務,而事務會銷燬同一個數據庫連接上以前打開的記錄集,具體可以參考微軟的文檔:

http://support.microsoft.com/default.aspx?scid=kb;en-us;187942

處理這種情況,一種解決方案是使用微軟建議的方式,使用client side cursor,不過這會大大降低數據庫訪問性能。

另一種解決方案是讓查詢和事務操作使用不同的數據庫連接,這需要在設計初期進行明確的定義。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章