怪異的COM 執行緒模型

j4exgdnter發表於2009-04-16

COM的本質是C/S,考慮執行緒安全的時候,也可以分別從ClientServer的角度考慮。從Client角度看,是如何安全的呼叫共享的Server;反之從Server角度看,是如何安全的對多執行緒的呼叫提供服務。以C++的角度看,Server就是一個C++類的例項,COM不過是很多機制幫助Client如何建立In-Process/Out Process的合適的C++例項。並在Out-Process的情況下,提供合適的Proxy/Stub來完成inter-process的呼叫。

[@more@]

Proxy/Stub的實質是RPC/IPC,具體實現上則依賴於Windows MessageQ。之所以依賴於MSGQ,是因為MSGQ獨特而齷齪的非標準化設計。在Linux/Unix系統上,當要等待多個外部事件,就是去select多個對應的fdWindows也提供select函式的支援,但是Windows MSGQ並不是一個可以被selectfd可以代替的,至少微軟公開的API不容許這樣的使用。RPC的介面能提供一個被selectfd嗎?不知道,應該不能。WindowsMessageQ,並不是一個被公開細節的Windows技術。對於擁有UIthread,要麼在等待MSGQ新訊息,要麼在處理訊息,MSGQ成為驅動其運轉的基礎。外部非同步的事件,一定要轉換為一個Windows訊息,並放在MSGQ上面才能被本執行緒相應。MFC非同步Socket就是這樣一個例子,所有的Socket事件實際上在另外一個執行緒裡面被轉換為一個Windows Message訊息後被送到UI執行緒上(建立Socket的那個UI執行緒)。

對於擁有UIthreadInterProcessCOM呼叫通訊由RPC完成;Client或者ServerCOM子系統之間則是由MSQ介面完成。Client呼叫InterProcessInterface的函式,即呼叫Proxy對應的函式,Proxy轉換呼叫為RPC呼叫,RPCServer後,被轉換為一個Windows MessageServerMSGQ裡面取訊息,處理訊息,並呼叫實際Interface的函式。返回的過程也大致相同,也即InterProcessRPC/IPC來完成。傳送方的請求(或者返回),在本地被轉換為一個RPC,在對端RPC被轉換為一個Windows訊息,插入到對端的訊息佇列,當訊息佇列訊息被處理時,請求就被執行或者返回完成。Apartment/STA/MTA概念的引入,是要使用者在不瞭解MSGQ細節的情況下理解如何設計執行緒安全的COM ClientServer

單個執行緒呼叫一個C++物件,當然是執行緒安全的(除非C++物件本來封裝了多個執行緒)。這對應著一個Interface在僅僅在其被建立的執行緒上呼叫。

對於DLL.COM來說,多個執行緒同時呼叫一個C++物件,當然是要考慮執行緒的安全問題(除非C++物件是完全可重入的,無狀態的)。要解決這樣的問題,方法有二。1STA,從Client角度,讓多個執行緒的呼叫序列化是一個辦法。簡單來說,當非COM物件建立者執行緒需要呼叫這個物件的Interface時候,就給建立者執行緒發個訊息,因為建立者執行緒上對訊息處理是序列的,所以並行的呼叫被序列化。CoMarshalInterThreadInterfaceInStream/ CoGetInterfaceAndReleaseStream 這兩個函式就是為這樣的序列化做準備的。這兩個函式實際上實際上要在兩個執行緒之間建立基於RPC/IPCProxy/Stub關係。呼叫完成後,誇執行緒的Interface呼叫,實際上和跨Process的呼叫沒有區別了。2MTA,從Server的角度,如果不是可重入無狀態的,那麼需要對設計上使用同步機制,保證併發被呼叫的執行緒安全。

對於EXE.COM來說,有兩種情況,即EXE.COM是否支援被併發的呼叫。如果支援,那麼Client這邊,怎麼呼叫都沒事。如果不支援,那麼就在EXE.COM的訊息佇列裡面序列化Client的併發呼叫,也就沒事了。這個做法和DLL.COMClient側序列化呼叫是一致的。

總之一句話,要麼序列化呼叫,要麼支援被併發呼叫。

N年前,就看到這個話題,一直鬧不明白,最近仔細看看,其實也並不複雜。感覺是微軟,故意把事情搞複雜,讓人不明白,其實是想為自己垃圾的MSGQ設計遮羞罷了。如果能夠理解齷齪的MSGQ,也就見怪不怪了。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/21398729/viewspace-1020719/,如需轉載,請註明出處,否則將追究法律責任。

相關文章