[转]COM线程模型-套间
[轉(zhuǎn)]COM線程模型-套間
COM線程模型-套間來源: http://blog.csdn.net/crybird/archive/2008/10/11/3057067.aspx 查找了好多資料,終于對(duì)套件這一概念有一點(diǎn)心得,趕緊記錄下來。
首先,只要遵守COM規(guī)范,不用COM庫(kù)也能編寫COM程序,那相當(dāng)于自己實(shí)現(xiàn)用到的COM庫(kù)函數(shù)。本篇COM如果單獨(dú)出現(xiàn),指COM庫(kù)。
1 進(jìn)程、線程回顧
《WINDOWS核心編程》對(duì)進(jìn)程和線程有深入解釋,一個(gè)程序運(yùn)行起來,需要一個(gè)進(jìn)程作為容器。進(jìn)程管理所有系統(tǒng)資源、內(nèi)存、線程等等。線程是CPU的調(diào)度單位,有自己的棧和寄存器狀態(tài)。程序最初創(chuàng)建的線程叫主線程,主線程可以創(chuàng)建子線程,子線程還可以創(chuàng)建子線程。
不同進(jìn)程之間是無法直接通信的,因?yàn)樗鼈冊(cè)谔摂M內(nèi)存中的地址不一樣。但操作系統(tǒng)通過LPC機(jī)制,可以完成不同進(jìn)程之間的通信。
COM在進(jìn)程間通信的方法是本地過程調(diào)用(LPC),因?yàn)椴僮飨到y(tǒng)知道各個(gè)進(jìn)程的確切邏輯地址,所以操作系統(tǒng)可以完成這一點(diǎn)。不同進(jìn)程間傳遞的參數(shù)需要調(diào)整,LPC技術(shù)可以完成普通數(shù)據(jù)的直接拷貝(甚至包括自定義類和指針),但對(duì)于接口參數(shù),COM實(shí)現(xiàn)了IMarshal接口以調(diào)整。
為了可以用同樣的方式和進(jìn)程外、遠(yuǎn)程組件通信,客戶端不直接和組件通信,而是和代理/存根通信,代理/存根是(而且必須是) DLL形式,能完成參數(shù)調(diào)整和LPC調(diào)用。代理存根不用自己寫,系統(tǒng)會(huì)自動(dòng)產(chǎn)生。
注:接口的調(diào)整,包括列集和散集兩種marshal/unmarshal。
2 COM線程模型
2.1 分清模型與實(shí)現(xiàn)
看過《Inside C++ Object Model》(中文名:深入C++對(duì)象模型;侯捷譯)的人都知道,C++對(duì)象模型有三種,各家編譯器都選擇其中效率最高的一種實(shí)現(xiàn)出來。另外兩種就留在了理論世界,實(shí)現(xiàn)出來沒有太大意義。提這個(gè)的原因,就是為了弄清楚這一點(diǎn):COM線程模型只是理論構(gòu)想,是一種抽象的數(shù)學(xué)模型,還要COM庫(kù)通過各種手段實(shí)現(xiàn)出來,才能為我們使用。
2.2 套間的由來
最開始的COM庫(kù),支持的使用組件的唯一模式是single-thread-per-process模式。這樣就避免了多線程的同步,而且組件執(zhí)行的線程肯定是創(chuàng)建它的線程。
然而組件對(duì)象真正的執(zhí)行環(huán)境很復(fù)雜。COM組件的執(zhí)行環(huán)境有兩種:單線程環(huán)境Single-Thread,多線程環(huán)境Multi-Thread。單線程要考慮執(zhí)行線程是否是創(chuàng)建組件的線程;多線程還要考慮并發(fā)、同步、死鎖、競(jìng)爭(zhēng)等問題。無論哪種環(huán)境,都要編寫大量的代碼以使COM組件對(duì)象正確的運(yùn)行。
為了使程序員減輕痛苦,COM庫(kù)決心提供一套機(jī)制來幫助程序員。如果我們都遵從這套機(jī)制,只要付出較少的勞動(dòng),就可以讓組件對(duì)象和COM庫(kù)一起完成工作。COM庫(kù)這套機(jī)制的核心技術(shù)就是“套間技術(shù)”。
2.3 COM的多線程模型
2.3.1 COM庫(kù)的規(guī)定
關(guān)于多線程問題方面,COM庫(kù)做出了如下規(guī)則(不是COM標(biāo)準(zhǔn),是COM庫(kù)為了簡(jiǎn)化多線程編程中對(duì)組件的調(diào)用而制定的):
1. COM庫(kù)提供兩種套間,單線程套間和多線程套間,COM組件的編寫者最好提供對(duì)應(yīng)的屬性(后面會(huì)提到),COM組件的使用者要在套間里創(chuàng)建和調(diào)用組件。
2. COM庫(kù)對(duì)所有的調(diào)用進(jìn)行參數(shù)調(diào)整(如果需要),不管是對(duì)進(jìn)程內(nèi)服務(wù)器的調(diào)用,還是對(duì)進(jìn)程外服務(wù)器的調(diào)用。
3. 線程內(nèi)調(diào)用、跨線程調(diào)用、跨進(jìn)程調(diào)用都用統(tǒng)一的方式。需要用代理的會(huì)用代理。
如此COM規(guī)定了COM庫(kù)、組件編寫者、組件使用者三方合作關(guān)系。COM庫(kù)進(jìn)行協(xié)調(diào)關(guān)系,會(huì)根據(jù)組件的能力,在不同環(huán)境(套間)中創(chuàng)建和調(diào)用組件;編寫者要說明組件可以生存的環(huán)境;調(diào)用者查詢接口,合理調(diào)用。
2.3.2 單線程套間STA
Single-threaded Apartments,一個(gè)套間只關(guān)聯(lián)一個(gè)線程,COM庫(kù)保證對(duì)象只能由這個(gè)線程訪問(通過對(duì)象的接口指針調(diào)用其方法),其他線程不得直接訪問這個(gè)對(duì)象(可以間接訪問,但最終還是由這個(gè)線程訪問)。
COM庫(kù)實(shí)現(xiàn)了所有調(diào)用的同步,因?yàn)橹挥嘘P(guān)聯(lián)線程能訪問COM對(duì)象。如果有N個(gè)調(diào)用同時(shí)并發(fā),N-1個(gè)調(diào)用處于阻塞狀態(tài)。對(duì)象的狀態(tài)(也就是對(duì)象的成員變量的值)肯定是正確變化的,不會(huì)出現(xiàn)線程訪問沖突而導(dǎo)致對(duì)象狀態(tài)錯(cuò)誤。
注意:這只是要求、希望、協(xié)議,實(shí)際是否做到是由COM決定的。這個(gè)模型很像Windows提供的窗口消息運(yùn)行機(jī)制,因此這個(gè)線程模型非常適合于擁有界面的組件,像ActiveX控件、OLE文檔服務(wù)器等,都應(yīng)該使用STA的套間。
2.3.3 多線程套間MTA
Multithreaded Apartments,一個(gè)套間可以對(duì)應(yīng)多個(gè)線程,COM對(duì)象可以被多個(gè)線程并發(fā)訪問。所以這個(gè)對(duì)象的作者必須在自己的代碼中實(shí)現(xiàn)線程保護(hù)、同步工作,保證可以正確改變自己的狀態(tài)。
這對(duì)于作為業(yè)務(wù)邏輯組件或干后臺(tái)服務(wù)的組件非常適合。因?yàn)樽鳛橐粋€(gè)分布式的服務(wù)器,同一時(shí)間可能有幾千條服務(wù)請(qǐng)求到達(dá),如果排隊(duì)進(jìn)行調(diào)用,那么將是不能想象的。
注意:這也只是一個(gè)要求、希望、協(xié)議而已。
2.3.4 COM+新增NA
COM+為了進(jìn)一步簡(jiǎn)化多線程編程,引入了中立線程套間概念。
NA/TNA/NTA,Neutral Apartment/Thread Neutral Apartment / Neutral Threaded Apartment。這種套間只和對(duì)象相關(guān)聯(lián),沒有關(guān)聯(lián)的線程,因此任何線程都可以直接訪問里面的對(duì)象,不存在STA的還是MTA的。
2.4 到底什么是套間
根據(jù)《COM技術(shù)內(nèi)幕》的觀點(diǎn),COM沒有定義自己新的線程模型,而是直接利用了Win32線程,或者說對(duì)其做了改造、包裝。線程間的同步也是直接用的Win32 APIs。
《COM本質(zhì)論》設(shè)這樣定義的:套間定義了一組對(duì)象的邏輯組合,這些對(duì)象共享一組并發(fā)性和沖入限制。每個(gè)COM對(duì)象都屬于某一個(gè)套間,一個(gè)套間可以包含多個(gè)COM對(duì)象。
MSDN上解釋說,可以把進(jìn)程中的組件對(duì)象想象為分成了很多組,每一組就是一個(gè)套間。屬于這個(gè)套間的線程,可以直接調(diào)用組件,不屬于這個(gè)套間的線程,要通過代理才能調(diào)用組件。
最直接的說,COM庫(kù)為了實(shí)現(xiàn)簡(jiǎn)化多線程編程的構(gòu)想,提出了套間概念。套間是一個(gè)邏輯上的概念,它把Win32里的線程、組件等,按照一定的規(guī)則結(jié)合在一起,并且以此提供了一種模式,用于多線程并發(fā)訪問COM組件方面。可以把套間看作COM對(duì)象的管理者,它通過調(diào)度,切換COM對(duì)象的執(zhí)行環(huán)境,保證COM對(duì)象的多線程調(diào)用正常運(yùn)行。COM和線程不是包含關(guān)系,而是對(duì)應(yīng)和關(guān)聯(lián)關(guān)系。
3 第一方COM庫(kù):模型的實(shí)現(xiàn)
3.1 單線程套間STA
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);這句代碼創(chuàng)建了一個(gè)STA,然后套間把當(dāng)前的線程和自己關(guān)聯(lián)在一起,線程被標(biāo)記為套間線程,只有這個(gè)線程能直接調(diào)用COM對(duì)象。
在創(chuàng)建套間的時(shí)候,COM創(chuàng)建了一個(gè)隱藏的窗口。關(guān)聯(lián)線程對(duì)組件的調(diào)用直接通過接口指針調(diào)用方法;其他線程對(duì)套間里的對(duì)象的調(diào)用,都轉(zhuǎn)變成對(duì)那個(gè)隱藏窗口發(fā)送消息,然后由這個(gè)隱藏窗口的消息處理函數(shù)來實(shí)際調(diào)用組件對(duì)象的方法。編寫組件代碼的時(shí)候,只需調(diào)用DispatchMessage即可將方法調(diào)用的消息和普通的消息區(qū)分開來(通過隱藏窗口的消息處理函數(shù))。
由于窗口消息的處理是異步的,所以所有的調(diào)用都是依次進(jìn)行的,不必考慮同步的問題。只要調(diào)用的時(shí)候,參數(shù)進(jìn)行合理調(diào)整即可(COM庫(kù)會(huì)做到這一點(diǎn))。但是對(duì)于全局變量和靜態(tài)變量,組件編寫者還是要費(fèi)心的。
一個(gè)STA只關(guān)聯(lián)一個(gè)線程, single-thread-per-process模式只是STA的一個(gè)特例。使用這種模式的線程叫套間線程(Apartment Thread)。
3.2 多線程套間MTA
CoInitializeEx(NULL, COINIT_MULTITHREADED);第一次如此調(diào)用的時(shí)候,會(huì)創(chuàng)建一個(gè)MTA,然后套間把當(dāng)前線程和自己關(guān)聯(lián)在一起,線程被標(biāo)記為自由線程。以后第二個(gè)線程再調(diào)用(在同一進(jìn)程中)的時(shí)候,這個(gè)MTA會(huì)把第二個(gè)線程也關(guān)聯(lián)在一起,并標(biāo)記為自由線程。一個(gè)MTA可以關(guān)聯(lián)多個(gè)線程。
所有的關(guān)聯(lián)線程都可以調(diào)用套間中的組件。這就涉及到同步問題,需要組件編寫者解決。
一個(gè)MTA可以關(guān)聯(lián)一個(gè)或多個(gè)線程,這種模式下,COM組件自己要考慮同步問題。使用這種模式的這些線程叫做自由線程(Free Thread) 。
3.3 總結(jié)
一個(gè)進(jìn)程可以有0個(gè)、1個(gè)或多個(gè)STA,還可以有0個(gè)或1個(gè)MTA。
一個(gè)線程,進(jìn)入(或創(chuàng)建)套間后,不能改變套間模式;但可以退出套間,然后以另外的模式再進(jìn)入(或創(chuàng)建)另一個(gè)套間。
在一個(gè)進(jìn)程中,主套間是第一個(gè)被初始化的。在單線程的進(jìn)程里,這是唯一的套間。調(diào)用參數(shù)在套間之間被調(diào)整,COM庫(kù)通過消息機(jī)制處理同步。
如果你設(shè)計(jì)多個(gè)線程作為自由線程,所有的自由線程在同一個(gè)單獨(dú)的套間中,參數(shù)被直接(不被列集)傳遞給這個(gè)套間的任何線程,而且你要處理所有的同步。
在既有自由線程又有套間線程的進(jìn)程里,所有自由線程在一個(gè)套間里,而其他套間都是單線程套間。而進(jìn)程是包含一個(gè)多線程套間和N個(gè)單線程套間的容器。
COM的線程模型為客戶端和服務(wù)器提供了這樣一種機(jī)制:讓不同的線程協(xié)同工作。不同進(jìn)程內(nèi),不同線程之間的對(duì)象調(diào)用也是被支持的。以調(diào)用者的角度來看,所有對(duì)進(jìn)程外對(duì)象的調(diào)用都是一致的,而不管它在怎樣的線程模型。以被調(diào)用者的角度來看,不管調(diào)用者的線程模型如何,所獲得的調(diào)用都是一致的。
客戶端和進(jìn)程外對(duì)象之間的交互也很直接,即使它們使用了不同的線程模型,因?yàn)樗鼈儗儆诓煌倪M(jìn)程。COM介入了客戶端和服務(wù)器之間,通過標(biāo)準(zhǔn)的列集和RPC,并提供了跨線程操作的代碼。
4 第二方COM組件的調(diào)用者
4.1 各種調(diào)用
4.1.1 同一線程中的調(diào)用
同步問題:不需要,調(diào)用者和組件在同一線程中,自然同步。
調(diào)整問題:不需要,COM庫(kù)不需要任何介入,參數(shù)也不需要調(diào)整,組件也不必線程安全。
調(diào)用地點(diǎn):當(dāng)前線程
這是最簡(jiǎn)單的情況。
4.1.2 套間線程之間的調(diào)用
同步問題:COM庫(kù)對(duì)調(diào)用進(jìn)行同步。
調(diào)整問題:不管兩個(gè)套間是否在同一進(jìn)程,都需要調(diào)整。某些情況下,需要手動(dòng)調(diào)整。
調(diào)用地點(diǎn): 對(duì)象所在套間線程。
4.1.3 自由線程之間的調(diào)用
同步問題:COM不進(jìn)行同步,組件自己同步。
調(diào)整問題:同一進(jìn)程不調(diào)整,不同進(jìn)程要調(diào)整。
調(diào)用地點(diǎn):客戶線程。
4.1.4 自由線程調(diào)用套間線程的組件
同步問題:COM庫(kù)對(duì)調(diào)用進(jìn)行同步。
調(diào)整問題:不管兩個(gè)套間是否在同一進(jìn)程,都需要調(diào)整。某些情況下,需要手動(dòng)調(diào)整。
調(diào)用地點(diǎn):套間線程
4.1.5 套間線程調(diào)用自由線程的組件
同步問題:COM不進(jìn)行同步,組件自己同步。
調(diào)整問題:需要調(diào)整,同一進(jìn)程,COM會(huì)優(yōu)化調(diào)整。
調(diào)用地點(diǎn):客戶線程。
4.2 手工調(diào)整
如果通過COM方法,所有的參數(shù)都由COM庫(kù)進(jìn)行調(diào)整。有時(shí)候需要程序員手工對(duì)接口指針進(jìn)行列集marshal和散集unmarshal,那就是在跨越套間邊界,但沒有通過COM庫(kù)進(jìn)行通信的時(shí)候。更明確的說,不通過COM接口函數(shù),通過我們自己寫的函數(shù)跨套間傳遞接口指針的時(shí)候。
情況一:跨套間傳遞接口指針。
情況二:類廠在另外的套間中,創(chuàng)建類實(shí)例,并傳回給客戶端的接口指針。
列集函數(shù):CoMarshalInterThreadInterfaceInStream
散集函數(shù):CoGetInterfaceAndReleaseStream
5 第三方COM組件的編寫者
組件將在哪種類型的套間中執(zhí)行,是編寫者決定的。對(duì)于進(jìn)程外組件,要調(diào)用CoInitializeEx并指定參數(shù),以顯示確定套間類型。對(duì)于進(jìn)程內(nèi)的服務(wù)器來說,因?yàn)榭蛻舳艘呀?jīng)調(diào)用CoInitializeEx產(chǎn)生套間了,為了允許進(jìn)程內(nèi)的服務(wù)器可以控制它們的套間類型,COM允許每個(gè)組件有自己不同的線程模型,并記錄在注冊(cè)表中。
HKEY_CLASSES_ROOT/CLSID/.../InprocServer32 鍵值ThreadingModel
5.1 線程模型屬性
組件編寫者可以實(shí)現(xiàn):同一個(gè)組件,既可以在STA中運(yùn)行,也可以在MTA中運(yùn)行,還可以在兩中環(huán)境中同時(shí)存在。可以說組件有一種屬性說明可以在哪種環(huán)境中生存,屬性名叫做“線程模型”(相當(dāng)于“隱藏”)也未嘗不可。COM+里真正引入了這個(gè)屬性,并叫做ThreadModel。這個(gè)屬性可以有如下取值:
1. Main Thread Apartment
2. Single Thread Apartment (Apartment)
3. Free Thread Apartment (Free)
4. Any Apartment (Both)
5. Neutral Apartment (N/A)
5.2 對(duì)象在哪個(gè)套間創(chuàng)建
下表中第一列為套間種類,第一行為對(duì)象線程模型屬性。那么,結(jié)果就是在這樣的套間中創(chuàng)建這樣的組件,組件在什么地方。在必要的時(shí)候,會(huì)創(chuàng)建一個(gè)代理,就是表中的宿主。
未指定
Apartment
Free
Both
Neutral
單線程
(非主)
主STA
當(dāng)前套間
MTA
當(dāng)前套間
NA
單線程
(主線程)
當(dāng)前套間
當(dāng)前套間
MTA
當(dāng)前套間
NA
多線程
主STA
宿主STA
MTA
MTA
NA
Neutral
單線程
主線程套間
宿主STA
(本線程)
MTA
NA
NA
Neutral
多線程
主線程套間
宿主STA
MTA
NA
NA
5.3 屬性的選擇
原則是根據(jù)組件的功能選擇:
如果組件做I/O,首選Free,因?yàn)榭梢韵鄳?yīng)其他客戶端調(diào)用。
如果組件和用戶交互,首選Apartment,保持消息依次調(diào)用。
COM+首選N/A。
如果沒有定義,COM庫(kù)默認(rèn)為是Main Thread Apartment。
Apartment簡(jiǎn)單,Free強(qiáng)大但要自己同步。
6 鳴謝
《COM技術(shù)內(nèi)幕》《COM本質(zhì)論》《深入解析ATL》
6.1 MSDN2008
在MSDN 2008中相關(guān)文檔的位置:
Win32和COM開發(fā)
-組件開發(fā)
-組件對(duì)象模型
-SDK文檔
-COM
-COM Fundamentals
-Guide-Processes, Threads, and Apartments
Win32和COM開發(fā)
-組件開發(fā)
-COM+
-SDK文檔
-COM+(組件服務(wù))
-COM+開發(fā)瀏覽
-COM+ Contexts and Threading Models
6.2 COM線程模型
http://hi.baidu.com/zhangqiuxi/blog/item/ca7aa52b0311b4fbe6cd401e.html
6.3 理解 COM 套間
http://www.vckbase.com/document/viewdoc/?id=1597
6.4 泛說"COM線程模型"
http://blog.csdn.net/guogangj/archive/2007/09/06/1774280.aspx
6.5 附泛說一文
COM線程模型在COM相關(guān)的基礎(chǔ)知識(shí)中應(yīng)該算是難點(diǎn),難的原因可能有這些:
1、需要對(duì)COM其它基礎(chǔ)知識(shí)有較深的了解,因?yàn)檫@個(gè)論題幾乎涉及到了COM所有其它的基礎(chǔ)知識(shí)。
2、學(xué)習(xí)者得非常了解Win32本身的線程模型,因?yàn)樵赪indows中COM的線程模型在建立在Win32線程模型的基礎(chǔ)上的。
3、COM線程模型所引用的概念十分抽象,不好理解。
如果你還沒有掌握 1,2 所提到的知識(shí)點(diǎn),你可以馬上找一些書籍,迅速補(bǔ)充這些知識(shí),如果你已經(jīng)掌握了這些知識(shí),那就給你的想象力上點(diǎn)油,輕松點(diǎn)。
6.5.1 開始想象
術(shù)語(yǔ)
公寓(Apartment)有的譯文譯作"套間"。這個(gè)術(shù)語(yǔ)抽象的是COM對(duì)象的生存空間,你還真的可以想象成公寓,線程就是住在公寓里的人。
單線程公寓(Single-Threaded Apartment STA) 這種房間是供有錢人住的單人間,設(shè)備齊全,服務(wù)周到。
多線程公寓(Multithreaded Apartment MTA) 住在這種房間里的人條件就差多了,那么多人就擠在一個(gè)大房間里頭,可是他們自強(qiáng)不息。個(gè)個(gè)健壯得不得了。
然后思考
單線程公寓與多線程公寓的本質(zhì)差別有哪些?
如果另一個(gè)人要和住在單線程公寓的人通信,不能直接去找他,哪怕你也住在高貴的單人間。但你可以打電話。提醒一下,電話每次只能同時(shí)與一個(gè)人說話(他們還沒有用到電話會(huì)議之類的服務(wù))。住在多線程公寓的人他們的房間有個(gè)大窗子,如果住在單人間(單線程公寓)的人想與他們通信,來窗口說就行,而且這個(gè)窗子比你想的要大,可以同時(shí)讓很多人對(duì)話。同一房間里的人不用說了,他們可以直通話。
6.5.2 回到現(xiàn)實(shí)
術(shù)語(yǔ)
1、公寓,如果從來就不用考慮線程同步的問題,就用不著這個(gè)概念了,可是 COM 決定支持強(qiáng)大的多線程,于是引入了這個(gè)概念,公寓決定了線程與外界通信的方式。每一個(gè)與COM對(duì)象打交道的線程必須先決定要進(jìn)入哪種公寓。
2、單線程公寓,這種公寓本身只能包含一個(gè)線程,通過調(diào)用CoInitialize(NULL)進(jìn)入。它有著與窗口類似的運(yùn)作方式,回想一下窗口的運(yùn)行方式:消息泵不斷的從消息隊(duì)列提取消息,然后調(diào)度給相應(yīng)的窗口函數(shù)。這樣做的好處是,窗口函數(shù)永遠(yuǎn)不會(huì)重入,也就是說永遠(yuǎn)不會(huì)有同步的問題。單線程公寓也用了同樣的運(yùn)作方式(所以該公寓中的線程的主函數(shù)必須有一個(gè)消息循環(huán)):對(duì)該公寓中線程所擁有的COM對(duì)象的調(diào)用被隊(duì)列化,只有當(dāng)一個(gè)調(diào)用結(jié)束后,另個(gè)調(diào)用才會(huì)開始。那么組件對(duì)象的代碼也是永遠(yuǎn)不會(huì)重入。
3、多線程公寓,這種公寓可以包含任意多的線程(具體數(shù)目由操作系統(tǒng)決定)。一個(gè)進(jìn)程里頭只能包含一個(gè)這種公寓,所有調(diào)用 CoInitializeEx(NULL, COINIT_MULTITHEADED)都會(huì)進(jìn)入這個(gè)公寓。對(duì)該公寓中線程所擁有的COM對(duì)象的調(diào)用是直接的(先不考慮跨進(jìn)程的情況),包括本公寓中的線程與其它的STA線程。
然后思考
單線程公寓與多線程公寓 的本質(zhì)差別有哪些?
單線程公寓實(shí)現(xiàn)同步,有很多COM庫(kù)的干預(yù),包括將外部的調(diào)用轉(zhuǎn)化成窗口消息,然后那個(gè)特別的隱藏窗口的窗口函數(shù)把窗口消息轉(zhuǎn)化成COM對(duì)象的函數(shù)調(diào)用。這樣的模型可以減小開發(fā)組件的難度,可是,卻犧牲了效率。多線程公寓把實(shí)現(xiàn)同步任務(wù)全部交給了組件自己,所以在這種公寓中生存的COM對(duì)象必須足夠健壯,考慮各種同步問題,不至于多個(gè)線程在調(diào)用對(duì)象的成員函數(shù)時(shí)會(huì)打架。
6.5.3 弄清它們的關(guān)系
弄清公寓,線程,對(duì)象的關(guān)系是很重要的,你弄清了嗎?如果你沒有弄清,那上面的這些也一定也是看得懵懵懂懂。公寓是這里面最大的單位,它是線程的容器。如果調(diào)用CoInitialize(0),COM庫(kù)會(huì)創(chuàng)建一個(gè)STA(注意,是"創(chuàng)建"),你的線程將屬于這個(gè)公寓,并且是這個(gè)公寓的唯一成員。如果 CoInitializeEx(NULL, COINIT_MULTITHEADED),而且是第一個(gè)要求進(jìn)入MTA的線程,COM庫(kù)會(huì)創(chuàng)建一個(gè)MTA,其它后面調(diào)用 CoInitializeEx(NULL, COINIT_MULTITHEADED)的線程會(huì)直接進(jìn)入(注意,我用的"進(jìn)入")已有MTA。本來線程是一個(gè)運(yùn)行的實(shí)體,不會(huì)分配資源,可是在 COM的線程模型里一個(gè)對(duì)象與創(chuàng)建它的線程是緊密相關(guān)的,稱對(duì)象歸屬于某個(gè)線程,至于這種歸屬關(guān)系是在COM庫(kù)內(nèi)怎么管理,我們先不去管它,以后我們把線程A創(chuàng)建的對(duì)象說成是線程A的對(duì)象就行了(有一個(gè)例外,得說說,有一種Single 類型的COM對(duì)象,這種對(duì)象基實(shí)就是COM在提出線程模型前的產(chǎn)物,這種對(duì)象總是歸屬于主STA線程,即第一個(gè)調(diào)用CoInitialize(0)的線程。)
| 在這一部分我將講解COM提出的各個(gè)類型的線程模型,并說明COM運(yùn)行時(shí)期庫(kù)是如何實(shí)現(xiàn)它們的。 本文講解COM提出的各個(gè)類型的線程模型,再說明COM運(yùn)行時(shí)期庫(kù)是如何實(shí)現(xiàn)它們的. 線程模型是一種數(shù)學(xué)模型,專門針對(duì)多線程編程而提供的算法,但也僅是算法,不是實(shí)現(xiàn)。本文講解COM提出的各個(gè)類型的線程模型,再說明COM運(yùn)行時(shí)期庫(kù)是如何實(shí)現(xiàn)它們的,就像說明Windows是如何實(shí)現(xiàn)線程這個(gè)數(shù)學(xué)模型的一樣,最后指明一下跨套間調(diào)用和各種類型套間編寫的要求以幫助理解。希望讀者對(duì)于Windows操作系統(tǒng)的線程這個(gè)概念相當(dāng)熟悉,對(duì)何謂“線程安全的”亦非常了解。
套間實(shí)現(xiàn)規(guī)則
如果讀者對(duì)自己多線程編程的技術(shù)沒有信心,建議最好不要編寫可以存在于MTA套間的組件,不過就不能獲得MTA的高性能了。 在編寫MTA時(shí)還應(yīng)該注意到線程親緣性(thread affinity)。沒有線程親緣性是指沒有任何線程范圍的成員變量,比如線程局部存儲(chǔ)(TLS)、窗口句柄等。也就是說在MTA中不能保存任何記錄著TLS內(nèi)存的指針或窗口句柄,如果保存將沒有意義(比如A線程記錄的內(nèi)存空間對(duì)B線程來說是無效的,因?yàn)門LS構(gòu)造了一個(gè)線程相關(guān)的內(nèi)存空間,就像每個(gè)進(jìn)程都有自己的私有空間)。而不幸地MFC在它的底層運(yùn)作機(jī)制的實(shí)現(xiàn)中大量使用了TLS,如模塊線程狀態(tài)、線程狀態(tài)等。正是由于這個(gè)原因,MFC不能編寫在MTA中運(yùn)行的組件。 NA 由于可能會(huì)多個(gè)線程同時(shí)訪問NA套間的對(duì)象,因此和MTA一樣,其不能有線程親緣性并需要保護(hù)每個(gè)成員和全局及靜態(tài)變量。而關(guān)于NA的輕量級(jí)代理,是由COM+運(yùn)行時(shí)期庫(kù)生成的,讀者完全不用操心(只需將那個(gè)組件的ThreadingModel鍵值賦值為“Neutral”即可)。 前面提到過有一種進(jìn)程內(nèi)組件的ThreadingModel鍵值可以被賦為“Both”,這種組件很像NA,哪個(gè)套間都可能直接訪問它,但只是可能,而NA組件是可以,這點(diǎn)可以從前面的那個(gè)進(jìn)程內(nèi)組件所屬套間的規(guī)則表中看出。這種組件可以支持一種稱作自由線程匯集器(FTM——Free Threaded Marshaler)的技術(shù),由于其與本文題目無關(guān),在此不表。當(dāng)Both的組件使用了自由線程匯集器時(shí),除了滿足MTA的要求以外(上面所說的線程安全保護(hù)和沒有線程相關(guān)性),還要記錄傳進(jìn)來的接口指針的中立形式(比如IStream*,通過CoMarshallInterface得到),以防止對(duì)客戶的回調(diào)問題。 最后只是提醒一下,有3個(gè)STA套間,STA1、STA2和STA3。STA1用CoMarshallInterface得到的IStream*傳到STA2中通過CoUnmarshalInterface得到的代理和在STA3中同樣通過CoUnmarshalInterface得到的代理不同,不能混用。因?yàn)楫?dāng)STA2和STA3調(diào)用在STA1的對(duì)象時(shí),STA1如果回調(diào)(連接點(diǎn)技術(shù)就是一種回調(diào))調(diào)用者,則STA2和STA3的代理能分別正確的指出需要讓哪個(gè)線程執(zhí)行回調(diào)操作,即向哪個(gè)線程發(fā)送消息,因此不能混用。 |
總結(jié)
以上是生活随笔為你收集整理的[转]COM线程模型-套间的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Delphi利用MSCOMM控件进行GP
- 下一篇: 面对小点点谷歌广告表示很无奈