閱讀195 返回首頁    go 阿裏雲 go 技術社區[雲棲]


COM的多線程模型

      COM的多線程模型是COM技術裏頭最難以理解的部分之一,很多書都有涉及但是都沒有很好的講清楚。很多新人都會在這裏覺得很迷惑,google大神能搜到一篇vckbase上的文章,但是個人建議還是不要看的好幾乎是胡說八道在亂搞。

      COM自己其實並沒有任何多線程模型,所以他用的多線程模型還是WIN32裏頭的那一套線程和同步對象。作為準備,這裏先簡單講一下WIN32的線程和同步。作為慣例一講WIN32的線程和同步對象就要把進程、線程這兩個東西講一遍,但是這裏不講,因為會看COM的對這部分已經很熟悉了,如果不熟悉的話建議也不要看COM了先回頭看看《Windows核心編程》和《Windows高級編程》。WIN32的線程可以分為兩種,UI線程和工作線程。UI線程是一種與一個窗口綁定的線程,其特點是包含一個窗口一個消息循環和一個窗口過程,由於消息循環的存在導致了其天生就具有一種同步機製:任何發送到該線程的消息都會被消息循環同步,不會有任何兩個或以上的消息同時被窗口過程處理,所有消息都會被消息循環串行化;工作線程則可以認為是一個函數在一個線程上的一次運行,這種線程不具備任何自帶的同步機製,如果要對兩個工作者線程實施某種同步則隻能使用WIN32的同步對象如CriticalSection或者Event等等。

      接下來看COM的多線程模型,從VS2005的ATL工程向導上可以看到COM多線程模型分為這麼幾類:單線程(Single)、套間(Apartment)、兩者(Both)、自由(Free)。這個部分個人覺得翻譯不是很好,單線程(Single)個人認為翻譯成單套間會比較好,原因後麵有具體描述,但是作為尊重MS向導或者不至於更加把這部分弄得混亂,下麵的術語還是引用MS向導上的講法並且我盡可能使用英文術語。

      可以看到COM多線程模型裏最多用到的兩個字是套間,那麼先解釋一下套間。套間可以根據他的英文想象一個房間,這個房間周圍有牆,所以要進到這個房間必須用一種手段來穿透(通過門或者類似的東西),而這個房間裏放的就是一個或者多個COM對象。對套間更理論性的解釋是,在一個套間內存在一個或多個COM組件,而套間之間存在有一個明確的界限,並且套間內隻存在唯一的一個套間線程,這個套間線程存在一個類似於消息循環(其實不應該用類似的,他就是一個隱藏了窗口的消息循環)來保證其天生所具有的同步性。看了這個定義你會覺得套間像什麼?沒錯,一個隻有一個主線程的Windows窗口應用程序進程。所以套間就是一個UI線程!做為UI線程他自然就能完成同步的功能。接下去分幾個部分來講這幾種COM線程模型。

 

一、單線程(Single)

      前麵講過這個模型最好是被翻譯成單套間的好,因為這種多線程模型並不是說COM組件隻能被用在單線程程序裏頭的,相反組件還是可以被正常的用在多線程程序裏的。這種模型的真實意義是即使你的程序是多線程的並且在每個線程裏都調用了CoInitalize(0,COINIT_APARTMENT),事實上在你的程序進程裏頭也隻創建一個套間,並且把所有的組件都放到這個套間裏頭並由這個套間所擁有的消息循環來保證同步性。

      或許這樣講不全麵,但是上麵一段確實講了一種最簡單的情況,就是所有的組件都按Single模型來創建。如果不是這樣會什麼情況呢,舉個例子說A、B、C三個組件按Single模型創建,D按Apartment模型創建,並且四個組件分別在TA、TB、TC、TD四個線程裏創建實例(每個線程都調用CoInitalize(0,COINIT_APARTMENT)來創建環境),那麼組件A、B、C運行在由TA創建的套間裏(TB、TC都沒有創建套間,TA是這個套間的套間線程),而組件D則獨立運行在TD創建的套間裏(TD是這個套間的套間線程),這裏一共就有了兩個套間。這樣應該是完整的情況了,再複雜的情況我想你都能推出來了。

二、套間(Apartment)

      這種模型與前一種模型很相似,可以都被認為是創建WIN32概念上的UI線程,但是不同的在於,Single模型無論你在多少個線程裏調用多少次CoInitalize(0,COINIT_APARTMENT)都隻創建一個套間,套間的套間線程是你第一次調用CoInitalize(0,COINIT_APARTMENT)的線程,而Apartment模型則是你在一個線程上調用一個CoInitalize(0,COINIT_APARTMENT)就創建一個套間,並且把這個線程作為套間線程。

三、自由(Free)

      這種模型就是工作者線程了,COM不再用消息循環來提供同步機製了。你要在多線程裏使用,OK,那你自己給他做同步機製(或者由組件開發者把組件做成線程安全的)。你要單線程裏使用,那更好無論如何都不需要同步了。

四、兩者(Both)

      這種模型保證了組件即能在套間模型使用也能在自由模型使用。例如說組件自身被創建為Apartment或者Single,但是使用者用CoInitalize(0,COINIT_MULITITHREAD)來創建環境,那麼COM自己會再創建一個線程用CoInitalize(0,COINIT_APARTMENT)來創建環境供組件運行,反之亦然。

 

      最後講一下跨套間調用的問題,從上麵的描述可以看到在Single和Apartment這兩種模型裏頭套間內調用或者通過COM機製套間之間的通信都會被同步化,但是如何跨套間調用呢,比如說我們經常做這種事情,把一個存在在套間內組件的接口指針作為線程參數傳遞到另外一個線程中使用,或者兩個組件存在於不同的套間中,但是由於連接點或者回調的原因需要互相調用。這個時候我們就需要使用proxy/stub機製,在傳遞接口指針之前調用CoMashalInterThreadInterface這個函數(我估計這個是所有WIN32API裏函數名最長的函數了,MS真不讓人過好日子-.-|||)來包裝接口指針給PS dll來傳遞,然後再那個調用的線程或者套間裏使用CoGetInterfaceAndReleaseStream來重新獲得被調度過的接口指針,這樣就能確保COM的線程同步機製能夠正常的運行。

 

      好了,全部講完了,如果還不清楚建議去看一下《COM技術內幕》裏的第十二章,這本書是我見過的所有書裏對這部分描述得最好的一本(勝過《ATL技術內幕》),特別是裏麵那幾張圖,對理解這些模型非常有幫助,這書網上有電子版。

本文轉自:https://blog.csdn.net/phil2036/article/details/3852949

最後更新:2017-04-03 22:15:29

  上一篇:go iOS開發那些事-nib實現標簽導航
  下一篇:go 給第三方dll強簽名