利用corba+tao可以很方便的建立起分布式的應用,乙個典型的應用是1個server端和多個client端進行雙向通訊。
這個時候要特別注意防止產生併發處理不當造成死鎖問題。
我在專案(使用ace+tao-5.2.5+1.2.5)中就碰到這樣乙個問題,折磨了我好幾天,最後才發現原因。
注意corba+tao的應用,server端的資訊傳送(下行通道)和資訊接收(上行通道)是在同乙個埠進行的,
如果通道被占用,corba+tao的預設方式是等待直到通道被釋放。
實際上,這就相當於對於乙個共享資源加上鎖。
大家都知道,併發程式在使用1個以上的鎖的時候,要注意各個執行緒(程序)必須以相同的順序來加鎖,解鎖,否則非常容易造成死鎖。
這樣,如果在server端對某些各個客戶端的共享資料進行訪問的時候,
為了進行併發處理控制而加上鎖,就很容易造成意想不到的死鎖問題。
原因就是沒有意識到corba+tao的通訊通道也存在乙個鎖,
如果使用的這些鎖的順序沒有保持一致,就會造成死鎖。
我們開發了1個分布式trap處理系統。
1臺pc上trap處理的server程式作為service在開機後被啟動,一直在執行,在指定的埠上監聽trap資訊。
各個需要處理trap資訊的應用程式,可以傳送連線請求到server上請求連線。
也可以在不需要處理trap資訊的時候,傳送斷開連線請求到server上請求斷開連線。
server上保留連線成功的各個client端的stub。
各台印表機上設定產生trap的時候,資訊傳送的目標位址和埠為上述trap處理server的ip位址和埠。
這樣各台印表機上由於各種錯誤(缺紙,卡紙,缺墨,等等)觸發trap資訊的時候,
這些trap資訊被傳送到我們的server的埠上,被我們的server處理程式捕捉,
server處理程式會通過corba來通知各個成功連線的客戶端。
在server端儲存了乙個成功連線的客戶端的佇列。
需要處理trap資訊的應用程式,傳送連線請求到server上,server端會往這個佇列裡面加上此客戶端的stub;
不需要處理trap資訊的時候,客戶端應用程式傳送斷開連線請求到server上,server端會從這個佇列裡面刪除此客戶端的stub;
此外,server端的接受trap資訊的執行緒會訪問這個佇列,取出裡面的每乙個客戶端的stub,向這個客戶端傳送trap通知。
上述的所有針對客戶端佇列的操作,都需要加上鎖來進行併發控制。
最開始的時候,我們設計的處理流程如下:
server端接收到客戶端的連線請求的時候(執行緒1)
1)客戶端傳送請求到server端的corba埠
2)使用的corba中介軟體自己進行一些處理
3)lock客戶端佇列的訪問鎖
4)檢查此客戶端是否已經鏈結過,如果沒有,把此客戶端的stub加入到此客戶端佇列
5)unlock客戶端佇列的訪問鎖
6) 使用的corba中介軟體自己的一些處理
7)返回請求成功給客戶端
server端接收到trap的時候(執行緒2)
1)lock客戶端佇列的訪問鎖
2)取得佇列中每乙個客戶端的stub
3)傳送trap通知資訊到client端的corba埠
4)client端處理此trap通知資訊
5)處理完畢,從client端返回
6)unlock客戶端佇列的訪問鎖
以上的處理,看上去沒有任何問題,
但是我們在進行多個客戶端反覆請求連線,請求斷開連線 + 產生大量trap通知
這樣的壓力測試的時候,就發現時間不長,server程式就停止處理了,
經過檢查log,確定是發生了死鎖。
原因就是corba+tao的通訊通道存在另外乙個鎖,
和我們自己加上的鎖,2個鎖的加鎖順序在多個執行緒中不一致。
可以參考下面的分析,corba的通訊通道鎖用藍色字型表示。
server端接收到客戶端的連線請求的時候(執行緒1)
1)客戶端傳送請求到server端的corba埠
a)lock corba的通訊通道鎖
2)使用的corba中介軟體自己進行一些處理
3)lock客戶端佇列的訪問鎖
4)檢查此客戶端是否已經鏈結過,如果沒有,把此客戶端的stub加入到此客戶端佇列
5)unlock客戶端佇列的訪問鎖
6) 使用的corba中介軟體自己的一些處理
7)返回請求成功給客戶端
b)unlock corba的通訊通道鎖
server端接收到trap的時候(執行緒2)
1)lock客戶端佇列的訪問鎖
2)取得佇列中每乙個客戶端的stub
a)lock corba的通訊通道鎖
3)傳送trap通知資訊到client端的corba埠
4)client端處理此trap通知資訊
5)處理完畢,從client端返回
b)unlock corba的通訊通道鎖
6)unlock客戶端佇列的訪問鎖
可以看到,
執行緒1加鎖的順序是先corba的通訊通道鎖 再 客戶端佇列的訪問鎖;
執行緒2加鎖的順序是先客戶端佇列的訪問鎖 再 corba的通訊通道鎖;
2個執行緒中的加鎖順序不一致,從而導致產生死鎖現象。
解決問題的方法是,
執行緒1的處理的第3步,進行lock的時候,
使用lock失敗即返回的方式進行呼叫,
lock失敗後就作為無法連線返回。
客戶端得到此錯誤,等待一定時間進行retry。
這樣就可以避免死鎖問題。也不影響程式正常執行。
多執行緒中使用fork 導致分頁
最近和同事一起處理了乙個 fuse 的大bug 首先看堆疊 core was generated by sf cluster bin pmxcfs program terminated with signal sigabrt,aborted.0 0x00007f2debdcc475 in raise...
python死鎖案例 Python 多執行緒死鎖
場景是這樣的 開啟多個執行緒訪問外部 api 過一段時間,發現所有執行緒死鎖。環境 python2.6.7 centos7.1 urllib2 suse 下無此問題。dump 如下 thread 161 thread 0x7f80de4e9700 lwp 12459 0 0x00007f80e4ca...
多執行緒導致資料重複新增
多執行緒有序執行的方法!參考 多執行緒和執行緒池的總結以及常見的面試問題 1.測試用例 test public void testthread throws interruptedexception t1.start t1.join list.clear thread t2 new thread n...