日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

[转]COM线程模型-套间

發(fā)布時(shí)間:2025/3/15 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转]COM线程模型-套间 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

[轉(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ì)何謂“線程安全的”亦非常了解。

  COM線程模型

  COM提供的線程模型共有三種:Single-Threaded Apartment(STA 單線程套間)、Multithreaded Apartment(MTA 多線程套間)和Neutral Apartment/Thread Neutral Apartment/Neutral Threaded Apartment(NA/TNA/NTA 中立線程套間,由COM+提供)。雖然它們的名字都含有套間這個(gè)詞,這只是COM運(yùn)行時(shí)期庫(kù)(注意,不是COM規(guī)范,以下簡(jiǎn)稱COM)使用套間技術(shù)來實(shí)現(xiàn)前面的三種線程模型,應(yīng)注意套間和線程模型不是同一個(gè)概念。COM提供的套間共有三種,分別一一對(duì)應(yīng)。而線程模型的存在就是線程規(guī)則的不同導(dǎo)致的,而所謂的線程規(guī)則就只有兩個(gè):代碼是線程安全的或不安全的,即代碼訪問公共數(shù)據(jù)時(shí)會(huì)或不會(huì)發(fā)生訪問沖突。由于線程模型只是個(gè)模型,概念上的,因此可以違背它,不過就不能獲得COM提供的自動(dòng)同步調(diào)用及兼容等好處了。

  STA 一個(gè)對(duì)象只能由一個(gè)線程訪問(通過對(duì)象的接口指針調(diào)用其方法),其他線程不得訪問這個(gè)對(duì)象,因此對(duì)于這個(gè)對(duì)象的所有調(diào)用都是同步了的,對(duì)象的狀態(tài)(也就是對(duì)象的成員變量的值)肯定是正確變化的,不會(huì)出現(xiàn)線程訪問沖突而導(dǎo)致對(duì)象狀態(tài)錯(cuò)誤。其他線程要訪問這個(gè)對(duì)象,必須等待,直到那個(gè)唯一的線程空閑時(shí)才能調(diào)用對(duì)象。注意:這只是要求、希望、協(xié)議,實(shí)際是否做到是由COM決定的。如上所說,這個(gè)模型很像Windows提供的窗口消息運(yùn)行機(jī)制,因此這個(gè)線程模型非常適合于擁有界面的組件,像ActiveX控件、OLE文檔服務(wù)器等,都應(yīng)該使用STA的套間。

  MTA 一個(gè)對(duì)象可以被多個(gè)線程訪問,即這個(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é)議而已。


  NA 一個(gè)對(duì)象可以被任何線程訪問,與MTA不同的是任何線程,而且當(dāng)跨套間訪問時(shí)(后面說明),它的調(diào)用費(fèi)用(耗費(fèi)的CPU時(shí)間及資源)要少得多。這準(zhǔn)確的說都已經(jīng)不能算是線程模型了,它是結(jié)合套間的具體實(shí)現(xiàn)而提出的要求,它和MTA不同的是COM的實(shí)現(xiàn)方式而已。
COM套間

  Apartment被翻譯成套間或是單元,是線程模型的一個(gè)實(shí)現(xiàn)者,就像在操作系統(tǒng)課程中講到的線程只是一個(gè)數(shù)學(xué)模型,而Windows的線程、進(jìn)程是它(數(shù)學(xué)模型的線程、進(jìn)程)的實(shí)現(xiàn)者。套間只是邏輯上的一個(gè)概念,實(shí)現(xiàn)時(shí)只是一個(gè)結(jié)構(gòu)(由COM管理)而已,記錄著相關(guān)信息,如它的種類(只能是上面那三個(gè),至少現(xiàn)在是),并由COM根據(jù)那個(gè)結(jié)構(gòu)進(jìn)行相應(yīng)的處理。下面說明這三種套間的實(shí)現(xiàn)方式:

  STA套間 一個(gè)套間如果是STA,那么那個(gè)套間有且只有一個(gè)線程和其關(guān)聯(lián),有多個(gè)對(duì)象或沒有對(duì)象和其關(guān)聯(lián),就像有多個(gè)線程和一個(gè)進(jìn)程關(guān)聯(lián)一樣,也就是說套間那個(gè)結(jié)構(gòu)和某個(gè)線程及多個(gè)對(duì)象之間有關(guān)系,關(guān)系具體是什么由COM說得算,幸運(yùn)的是COM正是按照上面的線程模型來定義互相之間關(guān)系的。根據(jù)上面的算法,很容易就知道只有這個(gè)線程可以訪問這個(gè)套間里的對(duì)象。

  COM是通過在STA套間里的線程中創(chuàng)建一個(gè)隱藏窗口,然后外界(這個(gè)套間外的線程)對(duì)這個(gè)對(duì)象的調(diào)用都轉(zhuǎn)變成對(duì)那個(gè)隱藏窗口發(fā)送消息,然后由這個(gè)隱藏窗口的消息處理函數(shù)來實(shí)際調(diào)用組件對(duì)象的方法來實(shí)現(xiàn)STA的規(guī)則的。之所以使用一個(gè)隱藏窗口是為了方便組件代碼的編寫——只需調(diào)用DispatchMessage即可將方法調(diào)用的消息和普通的消息區(qū)分開來(通過隱藏窗口的消息處理函數(shù))。外界對(duì)這個(gè)對(duì)象的調(diào)用都將轉(zhuǎn)變成對(duì)這個(gè)隱藏窗口的消息發(fā)送來實(shí)現(xiàn)同步。至于COM如何截獲外界對(duì)對(duì)象的調(diào)用,則是利于代理對(duì)象,后面再說明。

  值得注意的是,如果使用標(biāo)準(zhǔn)匯集法生成代理對(duì)象,則代理對(duì)象會(huì)根據(jù)是進(jìn)程內(nèi)還是進(jìn)程外的跨套間調(diào)用,來決定具體操作。如果外界線程和STA線程在同一進(jìn)程內(nèi),則代理對(duì)象將直接向STA線程中的隱藏窗口發(fā)送消息;如果不在同一進(jìn)程內(nèi)(包括遠(yuǎn)程進(jìn)程),代理對(duì)象將向RPC管理的一個(gè)線程池請(qǐng)求一個(gè)線程(RPC線程)來專門向另一進(jìn)程中的STA線程的隱藏窗口發(fā)送消息,而不是代理對(duì)象直接發(fā)送消息,以防止外界線程由于網(wǎng)絡(luò)等不穩(wěn)定因素而導(dǎo)致掛起。

  因?yàn)镃OM利用消息機(jī)制來實(shí)現(xiàn)STA,因此STA套間里的線程必須實(shí)現(xiàn)消息循環(huán),否則COM將不能實(shí)現(xiàn)STA的要求。

  MTA套間 這種類型的套間可以和多個(gè)線程及多個(gè)或沒有對(duì)象相關(guān)聯(lián)。根據(jù)上面的MTA模型,可知只有這個(gè)套間里的線程才能訪問這個(gè)套間里的對(duì)象,和STA不同的只是可以多個(gè)線程同時(shí)訪問對(duì)象。

  外界(不屬于這個(gè)套間的線程)對(duì)這個(gè)套間里的對(duì)象的調(diào)用將會(huì)導(dǎo)致調(diào)用線程(外界線程,也就是STA線程,因?yàn)镹A沒有線程)掛起,然后向RPC管理的一個(gè)線程池請(qǐng)求一個(gè)線程(RPC線程,并已經(jīng)進(jìn)入了這個(gè)MTA套間)以調(diào)用那個(gè)對(duì)象的方法。對(duì)象返回后,調(diào)用線程被喚醒,繼續(xù)運(yùn)行。雖然可以讓STA線程直接調(diào)用對(duì)象(而不用像前述的掛起等待另一個(gè)線程來調(diào)用對(duì)象),但這是必須的,因?yàn)榭赡軙?huì)有回調(diào)問題,比如這個(gè)MTA線程又反過來回調(diào)外界線程中的組件對(duì)象(假設(shè)客戶本身也是一個(gè)組件對(duì)象,這正是連接點(diǎn)技術(shù)),如果異步回調(diào)將可能發(fā)生錯(cuò)誤。

  反過來,MTA的線程訪問STA里的對(duì)象時(shí),COM將把調(diào)用轉(zhuǎn)換成對(duì)STA線程里那個(gè)隱藏窗口的一個(gè)消息發(fā)送,返回后再由COM轉(zhuǎn)成結(jié)果返回給MTA的線程(如果使用標(biāo)準(zhǔn)匯集法生成標(biāo)準(zhǔn)代理對(duì)象,則發(fā)生的具體情況就如上面STA套間所述)。因此STA和MTA都是只能由它們關(guān)聯(lián)的線程調(diào)用它們關(guān)聯(lián)的對(duì)象。而根據(jù)上面所說,當(dāng)MTA調(diào)STA或STA調(diào)MTA,都會(huì)發(fā)生線程切換,也就是說一個(gè)線程掛起而換成執(zhí)行另一個(gè)線程。這是相當(dāng)大的消耗(需要從內(nèi)核模式向用戶模式轉(zhuǎn)換,再倒轉(zhuǎn)好幾回),而NA就是針對(duì)這個(gè)設(shè)計(jì)的。

  NA套間 這種套間只和對(duì)象相關(guān)聯(lián),沒有關(guān)聯(lián)的線程,因此任何線程都可以直接訪問里面的對(duì)象,不存在STA的還是MTA的。

  外界(其實(shí)就是任何線程)對(duì)這個(gè)套間里面的調(diào)用都不需要掛起等待,而是進(jìn)入NA套間,直接調(diào)用對(duì)象的方法。NA套間是由COM+提供的,COM+中的每個(gè)對(duì)象都有一個(gè)環(huán)境和其相綁定,環(huán)境記錄了必要的信息,并監(jiān)聽對(duì)對(duì)象的每一次調(diào)用,以保證當(dāng)將對(duì)象的接口指針成員變量進(jìn)行傳遞或回調(diào)時(shí)其操作的正確性(保證執(zhí)行線程在正確的套間內(nèi),MTA線程就是通過將自己掛起以等待STA線程的消息處理完畢來保證的),從而避免了調(diào)用線程的掛起,因此這個(gè)代理(其實(shí)也就是環(huán)境的一部分)被稱作輕量級(jí)代理(相對(duì)于STA套間和MTA套間的重量級(jí)代理——需要掛起調(diào)用線程,發(fā)生線程切換)。

  這個(gè)輕量級(jí)代理并不是永遠(yuǎn)都不發(fā)生線程切換。當(dāng)NA對(duì)象里有個(gè)對(duì)指向一個(gè)STA對(duì)象的指針的調(diào)用而調(diào)用線程不是那個(gè)STA對(duì)象關(guān)聯(lián)的線程時(shí),調(diào)用將會(huì)轉(zhuǎn)成向被調(diào)用的STA對(duì)象的關(guān)聯(lián)線程發(fā)送消息,此時(shí)照樣會(huì)發(fā)生線程切換。同理,如果那個(gè)對(duì)象是MTA的,而調(diào)用線程是STA線程時(shí),依舊發(fā)生線程切換。不過除此以外的大多數(shù)情況(即不在NA對(duì)象的方法中調(diào)用另一個(gè)套間對(duì)象的方法)都不會(huì)發(fā)生線程切換,即使出現(xiàn)上面的情況也只有必要(MTA調(diào)NA再調(diào)MTA就不用切換)才切換線程。

  根據(jù)上面所說,STA其實(shí)和MTA邏輯上是完全一樣的,只是一個(gè)是關(guān)聯(lián)一個(gè)線程,一個(gè)是關(guān)聯(lián)多個(gè)線程而已。但把它們分開是必要的,因?yàn)榫€程安全就是針對(duì)是一個(gè)線程還是多個(gè)線程。而NA之所以不關(guān)聯(lián)線程是因?yàn)樗哪康氖窍厦婵缣组g調(diào)用時(shí)產(chǎn)生的線程切換損耗,關(guān)聯(lián)線程沒有任何意義。

  COM強(qiáng)行規(guī)定(不遵守也沒轍,因?yàn)槿荂OM實(shí)現(xiàn)套間的,根本沒有插手的余地)一個(gè)進(jìn)程可以擁有多個(gè)STA的套間,但只能擁有一個(gè)MTA套間和一個(gè)NA套間,我想這應(yīng)該已經(jīng)很容易理解了(要兩個(gè)MTA套間或NA套間干甚?)。
套間生成規(guī)則

  線程在進(jìn)行大多數(shù)COM操作之前,需要先調(diào)用CoInitialize或CoInitializeEx。調(diào)用CoInitialize告訴COM生成一個(gè)STA套間,并將當(dāng)前的調(diào)用線程和這個(gè)套間相關(guān)聯(lián)。而調(diào)用CoInitializeEx( NULL, COINIT_MULTITHREADED );告訴COM檢查是否已經(jīng)有了一個(gè)MTA套間,沒有則生成一個(gè)MTA套間,然后將那個(gè)套間和調(diào)用線程相關(guān)聯(lián)。接著在調(diào)用CoCreateInstance或CoGetClassObject等創(chuàng)建對(duì)象的函數(shù)時(shí),創(chuàng)建的對(duì)象將以一個(gè)特定規(guī)則決定和哪個(gè)套間相關(guān)聯(lián)(后敘)。這樣完成后,就完成了線程、對(duì)象和套間的關(guān)聯(lián)(或綁定)。

  前面提到的決定對(duì)象去向的規(guī)則如下。

  當(dāng)是進(jìn)程內(nèi)組件時(shí),根據(jù)注冊(cè)表項(xiàng)<CLSID>\InprocServer32\ThreadingModel和線程的不同,列于下表:

創(chuàng)建線程關(guān)聯(lián)的套間種類ThreadingModel鍵值組件對(duì)象最后所在套間
STAApartment創(chuàng)建線程的套間
STAFree進(jìn)程內(nèi)的MTA套間
STABoth創(chuàng)建線程的套間
STA""或Single進(jìn)程內(nèi)的主STA套間
STANeutral進(jìn)程內(nèi)的NA套間
MTAApartment新建的一個(gè)STA套間
MTAFree進(jìn)程內(nèi)的MTA套間
MTABoth進(jìn)程內(nèi)的MTA套間
MTA""或Single進(jìn)程內(nèi)的主STA套間
MTANeutral進(jìn)程內(nèi)的NA套間



  進(jìn)程內(nèi)的主STA套間是進(jìn)程中第一個(gè)調(diào)用CoInitialize的線程所關(guān)聯(lián)的套間(即進(jìn)程中的第一個(gè)STA套間)。后面說明為什么還來個(gè)進(jìn)程內(nèi)的主STA套間。

  當(dāng)是進(jìn)程外組件時(shí),由主函數(shù)調(diào)用CoInitializeEx或CoInitialize指定組件所在套間,與上面的相同,CoInitialize代表STA,CoInitializeEx( NULL, COINIT_MULTITHREADED );代表MTA,沒有NA。因?yàn)镹A是COM+提供的,而COM+服務(wù)只能提供給進(jìn)程內(nèi)服務(wù)器,因此只使用上面的注冊(cè)表項(xiàng)的規(guī)則決定DLL組件是否放進(jìn)NA套間,而沒有提供類似CoInitializeEx( NULL, COINIT_NEUTRAL );來處理EXE組件。而且如果可以使用CoInitializeEx( NULL, COINIT_NEUTRAL );將導(dǎo)致調(diào)用線程和NA套間相關(guān)聯(lián)了,違背了NA的線程模型,這也是為什么ThreadingModel鍵在<CLSID>\InprocServer32鍵下。

  跨套間調(diào)用

  STA線程1創(chuàng)建了一個(gè)STA對(duì)象,得到接口指針I(yè)ABCD*,接著它發(fā)起STA線程2,并且將IABCD*作為線程參數(shù)傳入。在線程2中,調(diào)用IABCD::Abc()方法,成功或者失敗天注定。由于線程2所在的STA套間不同于線程1所在的STA套間,這樣線程2就跨套間調(diào)用另一個(gè)套間的對(duì)象了。按照前述的STA規(guī)則,IABCD::Abc()應(yīng)該被轉(zhuǎn)成消息來發(fā)送,而如果如上面做法,可以,編譯通過,不過運(yùn)行就不保證了。

  COM之所以能夠?qū)崿F(xiàn)前面所說的那些規(guī)則(STA、MTA、NA),是因?yàn)榭缣组g調(diào)用時(shí),被調(diào)用的對(duì)象指針是指向一個(gè)代理對(duì)象,不是組件對(duì)象本身。而那個(gè)代理對(duì)象實(shí)現(xiàn)前述的那三個(gè)實(shí)現(xiàn)算法(轉(zhuǎn)成消息發(fā)送,線程切換等),而一般所說的代理/占位對(duì)象(Proxy/Stub)等其實(shí)都只是指進(jìn)行匯集工作的代碼(后述)。而按照上面直接通過線程參數(shù)傳入的指針是直接指向?qū)ο蟮?#xff0c;所以將不能實(shí)現(xiàn)STA規(guī)則,為此COM提供了如下兩個(gè)函數(shù)(還有其他方式,如通過全局接口表GIT)來方便產(chǎn)生代理:CoMarshalInterface和CoUnmarshalInterface(如果在同一進(jìn)程內(nèi)的線程間傳遞接口指針,則可以通過這兩個(gè)函數(shù)來進(jìn)一步簡(jiǎn)化代碼的編寫:CoMarshalInterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream)。
現(xiàn)在重寫上面代碼,線程1得到IABCD*后,調(diào)用CoMarshalInterface得到一個(gè)IStream*,然后將IStream*傳入線程2,在線程2中,調(diào)用CoUnmarshalInterface得到IABCD*,現(xiàn)在這個(gè)IABCD*就是指向代理對(duì)象的,而不是組件對(duì)象了。

  因此,前面所說過的所有線程模型的算法都是通過代理對(duì)象實(shí)現(xiàn)的。要跨套間時(shí),使用CoMarshalInterface將代理對(duì)象的CLSID和其與組件對(duì)象建立聯(lián)系的一些必要信息(如組件對(duì)象的接口指針)列集(Marshaling)到一個(gè)IStream*中,再通過任何線程間通信手段(如全局變量等)將IStream*傳到要使用的線程中,再用CoUnmarshalInterface散集(Unmarshaling)出接口以獲得指向代理對(duì)象的接口指針。因此之所以要獲得代理對(duì)象的指針是因?yàn)橄胧褂肅OM提供的線程模型(但在COM+中,這不是唯一的理由),如果不想使用大可不必這么麻煩(不過后果自負(fù)),并沒有強(qiáng)制要求必須那么做。

  當(dāng)線程1和線程2都是MTA時(shí),則可以像最開始說的那樣,直接傳遞IABCD*到線程2中,因?yàn)镸TA線程模型同意多個(gè)線程同時(shí)直接調(diào)用對(duì)象,線程1和線程2在同一個(gè)MTA套間中,而那個(gè)對(duì)象通過某種形式(如ThreadingModel = Free)向COM聲明了自己支持MTA線程模型。

  而當(dāng)a.exe的線程1和b.exe的線程2都是MTA時(shí),則依舊需要像上面那樣進(jìn)行接口指針的匯集(列集→傳輸→散集這個(gè)過程)以得到指向代理而非對(duì)象的指針,即使線程1和線程2都是在MTA套間中,卻是在兩個(gè)不同的MTA套間中,因此是跨套間調(diào)用,需要匯集操作。

  匯集代碼

  前面已經(jīng)說明了套間的規(guī)則都是通過對(duì)代理對(duì)象而非組件對(duì)象發(fā)起調(diào)用以截取對(duì)組件對(duì)象的調(diào)用由代理對(duì)象來實(shí)現(xiàn)的。代理對(duì)象要和組件對(duì)象交互,將方法參數(shù)傳遞給組件對(duì)象,需要使用到匯集技術(shù),也就是列集→傳輸→散集這個(gè)過程。

  列集(Marshaling)指將信息以某種格式存為流(IStream*)形式的操作;散集(Unmarshaling)則是列集的反操作,將信息從流形式中反還出來;傳輸則只是流形式的傳遞操作。

  這里經(jīng)常發(fā)生誤會(huì)。前面的CoMarshalInterface所做的列集,是將代理對(duì)象的CLSID及一些持久信息(用于初始化代理對(duì)象)格式化為一種格式(網(wǎng)絡(luò)數(shù)據(jù)描述——Network Data Representation)后放到一個(gè)流對(duì)象中,可以通過網(wǎng)絡(luò)(或其他方式)將這個(gè)流對(duì)象傳遞到客戶機(jī),由客戶通過CoUnmarshalInterface從傳來的流對(duì)象中反還出代理對(duì)象的CLSID和初始化用的一些持久信息,生成代理對(duì)象并使用持久信息初始化它以用于匯集操作。這就是發(fā)生誤會(huì)的地方——這里的匯集操作不同于上面的匯集操作,其匯集的是接口方法的參數(shù)而不是什么CLSID和一些初始化信息。

  因此CoMarshalInterface和CoUnmarshalInterface是用于匯集接口指針的,再準(zhǔn)確點(diǎn)應(yīng)該是用于生成代理對(duì)象的。代理對(duì)象應(yīng)由讀者自己實(shí)現(xiàn),用于匯集接口方法的參數(shù)。一般有兩種代理對(duì)象的實(shí)現(xiàn)方式:自定義匯集和標(biāo)準(zhǔn)匯集。

  對(duì)于自定義匯集,組件需實(shí)現(xiàn)IMarshal接口和一個(gè)代理組件(即完全實(shí)現(xiàn)真正組件所有接口的一個(gè)副本,實(shí)現(xiàn)了匯集方法參數(shù)及線程模型的規(guī)則,也必須實(shí)現(xiàn)IMarshal接口),并將這個(gè)代理組件在客戶機(jī)上注冊(cè),以保證代理對(duì)象的正確生成。注意:如果參數(shù)中有接口指針,必須用CoMarshalInterface和CoUnmarshalInterface進(jìn)行匯集,否則無法實(shí)現(xiàn)正確的線程模型,且代理組件是線程模型的實(shí)現(xiàn)者,這點(diǎn)組件必須自己保證(如發(fā)送消息等)。

  對(duì)于標(biāo)準(zhǔn)匯集,組件無需實(shí)現(xiàn)IMarshal接口及代理組件,代替的,組件則需要為自己生成一個(gè)代理/占位組件(Proxy/Stub),其由于可通過MIDL由IDL文件自動(dòng)生成,效率高,代碼的正確性有保證,因而被鼓勵(lì)使用。COM提供了一個(gè)標(biāo)準(zhǔn)代理對(duì)象的實(shí)現(xiàn),其通過聚合組件的代理/占位組件以表現(xiàn)出其好像是組件的代理對(duì)象。與自定義匯集一樣,需要將這個(gè)代理/占位組件在客戶機(jī)上注冊(cè)以保證代理對(duì)象的正確生成。

  至于這兩種匯集的具體工作機(jī)理,由于與本文無關(guān),在此不表,這里僅僅只為消除代理對(duì)象和代理/占位組件之間的混淆。

  注意:對(duì)于將運(yùn)行于NA套間的組件,由于COM+的強(qiáng)制要求,其必須使用標(biāo)準(zhǔn)匯集進(jìn)行代理對(duì)象的生成而不是自定義匯集(COM+運(yùn)行時(shí)期庫(kù)重寫了標(biāo)準(zhǔn)代理對(duì)象來截獲對(duì)組件對(duì)象的調(diào)用和其自身的某些特殊處理——如保證NA套間正確工作)。

套間實(shí)現(xiàn)規(guī)則

  如前面所說,COM的套間機(jī)制要成功,必須服務(wù)器(組件)、客戶和COM運(yùn)行時(shí)期庫(kù)三方面合力實(shí)現(xiàn),其中有任何一方不按著規(guī)矩來,將不能實(shí)現(xiàn)套間機(jī)制的功能,不過這并不代表什么錯(cuò)誤,套間機(jī)制不能運(yùn)作并不代表程序會(huì)崩潰,只是不能和其他COM應(yīng)用兼容而已。

  比如:對(duì)象中的屬性1在設(shè)計(jì)的算法中肯定不會(huì)被兩個(gè)以上的線程寫入,只是會(huì)被多個(gè)線程同時(shí)讀出而已,因此不用同步,可以用MTA,但對(duì)象的屬性2卻可能被多個(gè)線程寫入,因此決定使用STA。從而在客戶端,通過前面說的CoMarshalInterface和CoUnmarshalInterface將對(duì)象指針傳到那個(gè)只會(huì)寫入對(duì)象的屬性1的線程,其實(shí)這時(shí)就可以直接將對(duì)象指針傳到這個(gè)線程,而不用想上面那樣麻煩(而且增加了效率),但是就破壞了COM的套間規(guī)矩了——兩個(gè)線程可以訪問對(duì)象,但對(duì)象在STA套間中。所以?!!什么事都不會(huì)發(fā)生,因?yàn)橐呀?jīng)準(zhǔn)確知道這個(gè)算法不會(huì)捅婁子(線程訪問沖突),即使破壞COM的規(guī)矩又怎樣?!而且組件仍可以和其他客戶兼容,因?yàn)椴话匆?guī)矩來的是客戶,與組件無關(guān)。不過如果組件破壞規(guī)矩,那么它將不能和每一個(gè)客戶兼容,但并不代表它和任何客戶都不兼容。這里其實(shí)就是客戶和組件聯(lián)合起來欺騙了COM運(yùn)行時(shí)期庫(kù)。

  上面的例子只是想幫助讀者加深對(duì)套間的理解,實(shí)際中應(yīng)該盡量保持和COM規(guī)范的兼容性(但不兼容并不代表是錯(cuò)誤的)。客戶要做的工作前面已經(jīng)說過了(那兩個(gè)函數(shù)或全局接口表或其他只要正確的方式),下面說明組件應(yīng)該做的工作。組件可以存在于四個(gè)套間中(多了一個(gè)主STA套間),所需工作分別如下:

  STA 當(dāng)一個(gè)組件是STA時(shí),它必須同步保護(hù)全局變量和靜態(tài)變量,即對(duì)全局變量和靜態(tài)變量的訪問應(yīng)該用臨界段或其他同步手段保護(hù),因?yàn)椴僮魅趾挽o態(tài)變量的代碼可以被多個(gè)STA線程同時(shí)執(zhí)行,所以那些代碼的地方要進(jìn)行保護(hù)。比如對(duì)象計(jì)數(shù)(注意,不是引用計(jì)數(shù)),代表當(dāng)前組件生成的對(duì)象個(gè)數(shù),當(dāng)減為零時(shí),組件被卸載。此變量一般被類廠對(duì)象使用,還好ATL和MFC已經(jīng)幫我們實(shí)現(xiàn)了缺省類廠,這里一般不用擔(dān)心,但自定義的全局或靜態(tài)變量得自己處理。

  主STA 與STA唯一的不同是這是傻瓜型的,連靜態(tài)和全局變量都可以不用線程保護(hù),因?yàn)樗胁皇前踩L問靜態(tài)和全局變量的對(duì)象都通過主線程(第一個(gè)調(diào)用CoInitialize的線程)的消息派送機(jī)制運(yùn)行,因此不安全的訪問都被集中到了一個(gè)線程的調(diào)用中,因而調(diào)用被序列化了,也就實(shí)現(xiàn)了對(duì)靜態(tài)和全局變量的線程保護(hù)。至于為什么是主線程,因?yàn)檫M(jìn)程要使用STA,則一定會(huì)創(chuàng)建主線程,所以一定可以創(chuàng)建主STA。因此主STA并不是什么第四種套間,只是一個(gè)STA套間,不過關(guān)聯(lián)的是主線程而已,由于它可以被用作保護(hù)靜態(tài)和全局變量而被單獨(dú)提出來說明。因此一個(gè)進(jìn)程內(nèi)也只有一個(gè)主STA套間。

  MTA 必須對(duì)組件中的每個(gè)成員和全局及靜態(tài)變量的訪問使用同步手段進(jìn)行保護(hù),還應(yīng)考慮線程問題,即不是簡(jiǎn)單地保護(hù)訪問即可,還應(yīng)注意線程導(dǎo)致的錯(cuò)誤的操作,最經(jīng)典的就是IUnknown::Release()。


DWORD IUnknown::Release()
{
DWORD temp = InterlockedDecreament( &m_RefCount );
if( !temp ) // 不能用m_RefCount,原因請(qǐng)自己思考
delete this; // 因此不是只要用原子訪問函數(shù)保護(hù)了m_RefCount的訪問就行了
return temp; // 前面對(duì)全局變量的保護(hù)也和此類似,要考慮線程問題
}

  如果讀者對(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

中文字幕一区二区三区四区久久 | 免费看的黄色的网站 | 天天操比| 黄色性av | 日产av在线播放 | 女人18精品一区二区三区 | 亚洲精品国产精品99久久 | 国产成人一级 | 在线看的毛片 | 午夜精品一区二区三区可下载 | 又色又爽又激情的59视频 | 免费看成人a | 在线观看久久久久久 | 久久久男人的天堂 | 天天激情在线 | 国产专区视频 | 九九热精品视频在线播放 | 正在播放国产精品 | 99久久一区 | 精品国产aⅴ麻豆 | 亚洲国产中文在线观看 | 亚洲成人精品 | 成人一级视频在线观看 | 人人艹人人 | 国产高清av在线播放 | 亚洲午夜久久久久久久久电影网 | av在线免费观看网站 | 91.麻豆视频| 性色av香蕉一区二区 | 91麻豆国产福利在线观看 | 欧美a√在线 | av一区二区三区在线观看 | 亚洲午夜电影网 | 天天干夜夜爱 | 日韩成人精品一区二区三区 | 91丨九色丨国产女 | 久久免费视频在线观看30 | 日韩区欠美精品av视频 | 免费日韩电影 | 久久黄色美女 | 99色精品视频 | 色婷婷久久久 | 国产精品福利午夜在线观看 | 久久久国产精品麻豆 | 精品国产乱码久久久久久1区2匹 | 国产韩国日本高清视频 | 欧美婷婷色 | 99久久精品国产一区二区三区 | 国产精品免费观看久久 | 久久亚洲人 | 国产视频二区三区 | 开心综合网 | 97超碰资源 | 亚洲视频每日更新 | 激情伊人五月天久久综合 | 免费精品视频在线 | av在线激情 | www天天干com | 亚洲电影av在线 | 久久激情五月婷婷 | 日日夜夜亚洲 | 亚洲另类交 | 一区二区视频在线免费观看 | 一本色道久久综合亚洲二区三区 | 欧美日韩免费一区 | 成人在线观看影院 | 日韩成人精品在线观看 | 国产精品视频免费在线观看 | 久久艹影院 | 美女精品国产 | 插综合网 | 韩国av一区二区 | 三级黄色免费 | 黄色大片免费网站 | 日韩网站在线看片你懂的 | www91在线观看 | 少妇bbbb揉bbbb日本 | 精品你懂的 | 久久精品一区八戒影视 | 国产精品一级在线 | 丰满少妇一级 | 久久成人国产精品 | 欧美日韩在线观看一区 | 久久九九精品久久 | 日韩在线视频线视频免费网站 | 国产资源精品 | 色a在线观看 | 久久久影院官网 | 中文字幕资源网在线观看 | 中文在线中文a | 欧美999| 婷婷综合成人 | 99久久精品国产系列 | 久艹视频免费观看 | 成人av在线影院 | 91网站观看 | 国产不卡视频 | 日韩影视大全 | 免费看的视频 | 91视频大全 | 亚洲成色777777在线观看影院 | 91高清视频在线 | 中文字幕久久久精品 | 视频在线观看国产 | 亚洲一区视频在线播放 | 国产精品露脸在线 | 久久99亚洲精品 | 天天摸天天操天天爽 | 国产剧情在线一区 | 国产精品免费在线播放 | 婷婷色在线| 久久综合中文字幕 | 日韩在线电影一区二区 | 亚洲va欧美va人人爽 | av中文字幕日韩 | 中文免费观看 | www激情网 | 久久av伊人| 最近最新最好看中文视频 | 在线欧美中文字幕 | 亚洲天堂网在线播放 | 日韩天堂在线观看 | 国产在线精品一区二区不卡了 | 成人a级黄色片 | 日本在线观看视频一区 | 亚洲午夜精品在线观看 | 天堂av在线7 | 亚洲精品乱码久久久久久蜜桃91 | 国产毛片aaa | 91久久国产精品 | 国产美女精品视频 | 激情深爱 | 欧美老人xxxx18 | 黄色片网站大全 | www国产亚洲精品 | 国产色婷婷精品综合在线手机播放 | 国产黄色片免费在线观看 | 午夜av激情| 人人干干人人 | 国产精品久久久久久婷婷天堂 | 国产亚洲va综合人人澡精品 | 久草免费资源 | 国产在线毛片 | 久久久久久欧美二区电影网 | 精品99在线观看 | 最近中文字幕国语免费高清6 | www日韩视频 | 亚洲成人第一区 | 婷婷国产v亚洲v欧美久久 | 97干com| 一级成人免费 | 久久视频一区二区 | 超碰免费观看 | 天天草天天干天天 | 99久久这里只有精品 | 国产丝袜在线 | 国产精品毛片完整版 | 色在线视频 | 国产精品成人自产拍在线观看 | 成年在线观看 | 综合久久网 | 国产精品久久久久永久免费观看 | 国产一区福利 | 五月天.com | 国产黄色免费 | 日韩午夜视频在线观看 | 国产精品美女在线 | 2020天天干天天操 | 免费在线成人av | 久久精品日产第一区二区三区乱码 | 欧美日韩中文在线视频 | 欧美精品免费视频 | 亚洲精品久久久蜜臀下载官网 | 欧美孕交vivoestv另类 | 国产一区二区不卡在线 | 黄在线 | 91网页版在线观看 | 中文字幕网址 | 国产精品免费观看在线 | 国产一区二区在线免费播放 | 中文字幕在线视频国产 | 久久公开免费视频 | 精品国产伦一区二区三区观看说明 | 又湿又紧又大又爽a视频国产 | 久久精品在线 | 爱干视频 | 国产精品久久婷婷六月丁香 | 人人射av | 97成人在线观看视频 | 玖玖色在线观看 | 久久久久国产成人精品亚洲午夜 | 亚洲丝袜一区 | 国产精品久久久久久久久久ktv | 久操视频在线观看 | 天堂av观看 | 色综合久久综合 | 欧美国产一区二区 | 亚洲日韩中文字幕在线播放 | 日韩成人精品在线观看 | 国产青春久久久国产毛片 | 精品1区二区 | 免费a视频 | 高清不卡一区二区三区 | 成人观看 | 国产精品久久久久9999 | 91免费的视频在线播放 | 五月天久久综合网 | 在线观看国产一区二区 | av丝袜制服 | 又黄又爽又色无遮挡免费 | 国产一区二区在线观看视频 | 日本性视频 | 二区在线播放 | 亚洲精品视频在线观看免费视频 | 在线不卡中文字幕播放 | 99re视频在线观看 | 欧美一级裸体视频 | 久久精品免费 | 精品字幕在线 | 中文字幕在线有码 | 欧美激情另类文学 | 91精品国产高清自在线观看 | 精品久久久久亚洲 | 日本韩国在线不卡 | 天天躁天天狠天天透 | 久久久久欧美精品999 | 日韩精品首页 | 91在线91| 国产 一区二区三区 在线 | 日韩视频二区 | 伊人天堂av| 九九亚洲视频 | 99精品一级欧美片免费播放 | 国产在线精品一区二区不卡了 | 午夜国产在线观看 | 在线观看中文字幕一区二区 | 美女网站视频免费黄 | 久久看片网 | 免费观看性生活大片 | 亚洲精品久久激情国产片 | 中文字幕精品视频 | 99免费视频 | 91九色国产在线 | 日韩中文字幕在线观看 | 我要色综合天天 | 色全色在线资源网 | 在线国产中文 | 91国内在线 | 久久久久久国产精品久久 | 美女视频黄色免费 | 最新日韩视频在线观看 | 久久精品视频观看 | 久久99亚洲精品久久久久 | 黄网站色视频免费观看 | 国产午夜精品一区二区三区四区 | 婷婷五月情| 国产黄视频在线观看 | 欧美日韩免费一区二区 | 欧美一级看片 | 99视频精品免费视频 | 伊人影院得得 | 在线观看日韩 | 亚洲一区av | 久久久福利影院 | 久久精品久久精品久久 | 在线视频久久 | 黄色精品在线看 | 日韩av一卡二卡三卡 | 成人精品视频久久久久 | 91最新网址 | 亚洲电影成人 | 日韩精品一区在线播放 | 四虎成人精品永久免费av九九 | 亚洲精品视 | 国产尤物在线 | 在线观看韩日电影免费 | 久久精品高清 | 69av免费视频 | 91九色视频网站 | 亚洲精品中文在线 | 国产成人一区在线 | 国产美腿白丝袜足在线av | 国产精品久久久久久久久久 | 亚洲精品999| 91九色视频 | 久久国产免费看 | 国产伦精品一区二区三区在线 | 久久久精品欧美一区二区免费 | 国产精品午夜在线 | 88av视频 | 国产成人精品一区二 | 久久久精品国产一区二区三区 | 激情欧美国产 | 亚洲性xxxx| 国产精品成人在线观看 | 视频一区二区在线 | 久久久久久久国产精品视频 | 国产裸体永久免费视频网站 | 欧美专区日韩专区 | 免费精品视频 | 亚洲视频www| 国产亚洲人 | 91人人在线 | 天天操天天透 | 亚洲精品麻豆视频 | 欧美午夜a | 国产人成一区二区三区影院 | 欧美极品一区二区三区 | 天天综合网在线观看 | 婷婷av色综合 | 天天在线免费视频 | 91看片在线播放 | 一区免费视频 | 免费日韩av片 | 美女免费电影 | 五月婷婷av| 欧美视频一区二 | 黄色一级免费网站 | 久久人操| 青草视频在线播放 | 高清一区二区 | 国产在线a不卡 | 91精品毛片| 欧美日韩在线网站 | 日韩高清免费在线观看 | 亚洲 欧美 日韩 综合 | 最新日韩中文字幕 | av大片免费在线观看 | 91伊人| 亚洲一区二区三区四区在线视频 | 超碰免费97 | 日韩三级精品 | 一区二区三区在线免费观看视频 | 亚洲精品国偷自产在线91正片 | 亚洲国产视频网站 | 国产99久久九九精品免费 | 国产99久久九九精品免费 | 丁香综合av | 欧美在线视频一区二区三区 | 在线免费黄色 | 国产真实精品久久二三区 | 久久久久久久久电影 | 三级a毛片 | 国产又粗又硬又长又爽的视频 | 国产精品密入口果冻 | 久久精选| 久久久美女 | 18国产精品白浆在线观看免费 | 国产成人精品999在线观看 | 亚洲成年人av | 在线免费中文字幕 | 91天天操 | 狠狠干狠狠久久 | 在线免费av播放 | 中文字幕黄色av | 亚洲成a人片77777潘金莲 | 欧美91在线| 亚洲一级电影视频 | 免费网站在线 | 99久久精品国产亚洲 | 成人av在线网 | 亚洲精品18日本一区app | 五月婷亚洲 | 国产精品久久久毛片 | 色综合久久综合 | 亚洲,播放 | 黄色国产精品 | 天堂av免费 | 亚洲涩涩一区 | 国产99久久精品一区二区永久免费 | 日韩毛片一区 | 一区二区三区高清不卡 | .国产精品成人自产拍在线观看6 | 欧美国产日韩在线观看 | 在线观看亚洲电影 | 久久高清免费 | 色网免费观看 | 手机在线永久免费观看av片 | 亚洲不卡在线 | 正在播放 久久 | 午夜精品一区二区三区可下载 | japanesexxxxfreehd乱熟| 免费成人在线视频网站 | 亚洲欧美日韩在线一区二区 | 久久久久国产a免费观看rela | 免费国产一区二区视频 | 日本不卡视频 | 91欧美日韩国产 | 中文在线免费观看 | 中文字幕欲求不满 | 91精品免费在线观看 | 日本深夜福利视频 | 天天爱天天操天天爽 | 亚洲国产日韩一区 | 成人在线视频免费 | 欧美日在线观看 | 亚洲免费av观看 | 不卡的av| 国产亚洲精品成人av久久ww | 五月婷婷视频在线 | 欧美一区二区三区在线看 | 国产精品久久久久久久久搜平片 | 99自拍视频在线观看 | 国产精品综合久久 | 久久人人艹 | 五月综合网站 | 免费在线国产视频 | 成人九九视频 | 在线观看一级 | 插插插色综合 | 久久精品国产免费 | 久久男女视频 | 在线成人免费av | 九九视频精品在线 | 综合精品久久 | 婷婷久久五月天 | 成年人黄色免费看 | 午夜精品一区二区三区免费 | 国产青草视频在线观看 | 久久热首页 | 91精品久久久久久久久久入口 | 亚洲六月丁香色婷婷综合久久 | 国产99区 | 久久亚洲美女 | 久久国产经典视频 | 成人av免费| 天天操夜夜操夜夜操 | 天天操天天色天天射 | 亚洲一区二区视频在线播放 | 永久精品视频 | 国产亚洲字幕 | 8x成人免费视频 | 欧美综合在线视频 | 狠狠狠狠狠狠狠狠 | 欧美一区在线看 | 99视频在线 | 日色在线视频 | 日韩电影一区二区在线观看 | 日韩专区在线观看 | 亚洲欧洲精品久久 | 免费看片色 | 日韩精品久久中文字幕 | 黄色www| 黄色一级网 | 久久久久久美女 | 色香天天| 日韩精品一区二区三区不卡 | 国产一级视频在线观看 | 人人要人人澡人人爽人人dvd | 欧美伦理一区二区 | 91精品国产电影 | 国产精品美女免费看 | 国产综合91| 国产精品日韩在线观看 | 国产91精品久久久久 | 亚洲成人频道 | 色老板在线 | 1000部国产精品成人观看 | 欧美午夜久久 | 国产一区二区精品91 | 久久久资源网 | 岛国大片免费视频 | a级一a一级在线观看 | 91精品入口 | 美女黄网站视频免费 | 草莓视频在线观看免费观看 | 四川妇女搡bbbb搡bbbb搡 | 日韩xxx视频 | 黄色在线看网站 | 在线观看中文字幕第一页 | 欧美色图30p | 最新中文字幕在线观看视频 | 在线国产欧美 | 92精品国产成人观看免费 | 中文字幕色婷婷在线视频 | 天天综合色天天综合 | 亚洲一区二区91 | 国产精品久久av | 91亚·色 | 日批视频在线播放 | 免费美女av | 97视频在线播放 | 日韩av不卡在线播放 | 国产黄av | 国产精品成人a免费观看 | 久久久国产精品一区二区三区 | 国产99久久九九精品免费 | 西西4444www大胆艺术 | 九色视频网 | 天堂视频一区 | 国产精品99精品久久免费 | 成人免费亚洲 | 91男人影院 | 久久情网 | 91精品夜夜 | 久久免费视频在线观看30 | 天天摸天天操天天舔 | 午夜av一区 | 欧美日韩视频免费 | 欧美在线视频一区二区 | 五月花激情 | 激情丁香在线 | 国内偷拍精品视频 | 婷婷在线视频观看 | av三级在线看 | japanese黑人亚洲人4k | 日韩r级电影在线观看 | 一级免费av | 国产精品18久久久久久久久久久久 | 久久国产精品视频观看 | 看片网站黄 | 九九精品无码 | 激情视频91| 午夜久久久久 | av网站在线观看播放 | 色婷婷天天干 | 狠狠狠色丁香综合久久天下网 | 日韩在线免费不卡 | 欧美一区二区在线免费看 | 国产精品久久久一区二区三区网站 | 精品电影一区 | 免费看三片 | 不卡av电影在线观看 | 五月激情姐姐 | 久久综合色天天久久综合图片 | 精品国产理论片 | 欧美日韩高清一区 | 久久视频在线观看免费 | 高清中文字幕 | 色干综合| 国产在线视频一区二区三区 | 日本黄区免费视频观看 | 亚洲精品一区二区三区四区高清 | 亚洲视频在线观看免费 | 伊人天天干 | 91成人精品视频 | 午夜91视频 | 日韩一区二区三区高清在线观看 | 天天干人人 | 蜜桃av久久久亚洲精品 | 狠狠色丁香婷婷综合视频 | 精品视频在线看 | 亚洲综合激情五月 | 亚洲最新视频在线播放 | 国产精品国产三级国产aⅴ9色 | 国产白浆在线观看 | 最新中文在线视频 | 日本精品va在线观看 | 亚洲欧美日韩精品久久久 | 久久这里有精品 | 免费看一级特黄a大片 | 六月激情久久 | 91毛片在线| 国产视频在线观看一区 | 国产视频在线播放 | 91天天视频 | 97精品国产aⅴ | 亚洲一区二区精品 | 亚洲国产色一区 | 成年性视频| 日韩中文在线字幕 | 狠狠操电影网 | av黄色国产 | 久久全国免费视频 | 亚洲天堂网在线观看视频 | 久久综合偷偷噜噜噜色 | 欧美aa在线| 亚洲电影免费 | 黄色大片中国 | 亚洲精品五月天 | 国产乱对白刺激视频在线观看女王 | 久久av影视 | 99久久久久久国产精品 | 久久理论影院 | 亚洲天堂在线观看完整版 | 国产高清网站 | 久草久草在线 | 亚洲一本视频 | 免费av的网站 | 日韩精品视频在线观看免费 | 婷婷网五月天 | 东方av免费在线观看 | 国产福利久久 | 99av国产精品欲麻豆 | 欧美日韩国产高清视频 | 人人狠狠综合久久亚洲婷 | 久久久久久免费毛片精品 | 日日干日日色 | 天天射天天射天天射 | 国产一区二区不卡视频 | 999久久久久 | 香蕉蜜桃视频 | 天天天综合网 | www.亚洲精品视频 | 久久国产精品视频免费看 | 亚洲久草视频 | 成人免费网视频 | 日日夜夜天天射 | 免费观看全黄做爰大片国产 | 亚欧日韩成人h片 | www.黄色片网站 | 精品国产黄色片 | 亚洲黄色一级大片 | 色婷婷午夜 | 日日天天| 永久av免费在线观看 | 免费av片在线 | 高清不卡一区二区三区 | 最近乱久中文字幕 | 日日夜夜干 | www五月天com | 亚洲狠狠干 | 天天摸天天舔 | 欧美日韩中文视频 | 在线看成人片 | 免费高清在线观看成人 | 永久免费视频国产 | av中文字幕第一页 | 成人av在线一区二区 | 久草在线综合 | 日韩精品在线免费观看 | 国产流白浆高潮在线观看 | 成年人免费看片网站 | 午夜aaaa | 中文字幕一二三区 | 国产尤物一区二区三区 | 粉嫩高清一区二区三区 | 午夜精品av| 久久综合电影 | 欧美黑人巨大xxxxx | 精品99视频 | 8x成人在线| 在线视频一区观看 | 日韩精品一区二区免费 | 九九视频热 | 日韩电影精品一区 | 日韩中文字幕在线观看 | 中文字幕专区高清在线观看 | 久久激情五月丁香伊人 | 成人a在线观看高清电影 | 亚洲国产精品成人va在线观看 | 久久久天堂 | 91一区在线观看 | 国产精品99久久久久久武松影视 | 国产精品igao视频网网址 | 伊人狠狠色 | 女人18毛片a级毛片一区二区 | 免费精品 | 天天爱天天色 | 国产午夜小视频 | 黄色三级免费观看 | 久久电影色 | 精品国产一区二区三区蜜臀 | 中文在线a∨在线 | av不卡免费在线观看 | 日本中文字幕在线电影 | 日韩中文免费视频 | 狠狠干夜夜操天天爽 | 91香蕉视频污在线 | 国产婷婷视频在线 | 国产第一福利 | 亚洲精选99| 免费三级av | 国产午夜精品一区二区三区欧美 | 在线观看爱爱视频 | 国内成人精品2018免费看 | 日韩av成人 | 中文字幕三区 | 国产人成看黄久久久久久久久 | 日韩伦理片一区二区三区 | 激情丁香5月| 亚洲精品午夜aaa久久久 | 国产精品高清av | 玖玖玖国产精品 | 亚洲综合情 | 精品一二| 91人网站 | 久久激情电影 | 天天操天天色天天射 | 91免费高清| 中国美女一级看片 | 91传媒免费在线观看 | 久久久国产精品一区二区三区 | 亚洲黄色av网址 | 久久成年人 | 亚洲五月 | 久久艹99 | 98久9在线 | 免费| 黄色在线网站噜噜噜 | 国产在线观看一区 | www.99热精品| 狠狠干夜夜 | 欧美伦理一区二区 | 国产91免费观看 | 日日夜夜天天 | 日韩,精品电影 | 成人在线播放视频 | 又黄又色又爽 | 色婷婷www| 亚洲精品日韩av | 欧美日韩国产二区三区 | 欧美性色综合网站 | 国产亚洲一级高清 | 成人禁用看黄a在线 | 日韩在线视频看看 | 伊人va| 久久免费视频国产 | 人人插人人玩 | 日韩最新av | 九九热久久久 | 香蕉视频啪啪 | av在线网站观看 | 特级毛片网站 | 成人网中文字幕 | 久久久久99精品国产片 | 久久电影中文字幕视频 | 四虎免费在线观看视频 | 国产精品久久久久久久久久三级 | 国产男女爽爽爽免费视频 | 69亚洲视频| 在线黄色国产电影 | 久久综合九色99 | 五月婷香 | 国产精品女同一区二区三区久久夜 | 国产在线 一区二区三区 | 视频在线播放国产 | 天天曰天天曰 | 亚洲精品三级 | 天天爱天天射天天干天天 | 在线播放亚洲 | 亚洲人成免费网站 | 不卡在线一区 | 人人dvd | 草久久久 | 国产成人a v电影 | 国产一区二区三区四区大秀 | 在线亚洲小视频 | av在线进入 | 亚洲综合在线发布 | 伊人色播 | 久久影院亚洲 | 国产精品一区二区三区观看 | 在线观看一区二区精品 | 欧美日韩视频在线观看免费 | 中文字幕在线观看视频网站 | www久久精品 | 国产99久久九九精品 | 在线观看中文字幕网站 | 亚洲精品免费观看视频 | 久草在线观 | 狠狠色丁香婷婷综合久久片 | 免费碰碰 | 日本久热 | 亚洲视频精品在线 | 热re99久久精品国产66热 | 在线观看国产永久免费视频 | 国产91在线播放 | a亚洲视频 | 人人超碰免费 | 天天草天天干天天 | 久久精久久精 | 免费在线播放黄色 | 91中文在线| 91精品国产自产老师啪 | 国产资源免费在线观看 | 人人爽影院 | 免费看片在线观看 | 久久99最新地址 | 国产在线播放一区二区三区 | av中文字幕网址 | 日韩午夜一级片 | 91成年人视频 | 亚洲最新合集 | 久久精品视频网 | 天堂av在线中文在线 | 色资源中文字幕 | 91九色视频在线播放 | 日韩久久精品一区 | 亚洲欧美乱综合图片区小说区 | 天天做综合网 | 69av免费视频 | 免费特级黄色片 | 国内精品久久久久影院日本资源 | 国产 欧美 日产久久 | 91在线视频观看 | 91麻豆精品国产91久久久更新时间 | 成人香蕉视频 | 成年人免费电影 | 国内外成人免费在线视频 | 亚洲欧洲日韩在线观看 | 美女视频久久 | www.激情五月.com | 久久激情五月婷婷 | 国产高清黄色 | 狠狠色丁香婷婷综合欧美 | 亚洲成免费 | 久久公开视频 | 99精品欧美一区二区 | av成人资源| 日韩高清久久 | 激情视频免费在线 | 精品国产_亚洲人成在线 | 成人av网站在线播放 | 99精品热 | 国产专区精品 | 在线观看网站av | 日本精品在线视频 | 国产日韩欧美在线 | 久久免费精品一区二区三区 | 成人免费在线电影 | 欧美久久久久久久久久久久 | 欧美日韩国产mv | 中文字幕成人av | 欧美在线视频二区 | 中文字幕在线观看91 | 成人在线视频论坛 | 中文字幕第一页在线vr | 亚洲精品欧洲精品 | 日韩剧情| 91麻豆精品国产91久久久久久 | 欧美在线视频一区二区三区 | 天天天色综合a | 精品三级av | 国产一区在线播放 | 最近2019中文免费高清视频观看www99 | 丁香九月婷婷综合 | 中文字幕乱码电影 | 婷婷丁香激情网 | 91视频电影 | 日韩电影中文字幕 | 国产福利免费在线观看 | 国产一级91 | 欧美极品xxxxx | 国产又粗又硬又爽视频 | 少妇bbbb搡bbbb搡bbbb | 99精品视频在线播放观看 | 久久99操 | 亚洲女欲精品久久久久久久18 | 亚洲欧美日韩国产一区二区三区 | 日本一区二区三区免费观看 | 五月天电影免费在线观看一区 | 免费观看午夜视频 | 狠狠色丁香久久婷婷综合_中 | 69国产精品成人在线播放 | 麻豆视频成人 | 国产小视频国产精品 | 人人躁| 国产成人精品综合久久久久99 | av电影免费在线看 | 国内视频| 中文 一区二区 | 久久免费播放视频 | 色综合久久久久综合体 | 五月亚洲婷婷 | av高清影院| 国产精品自拍在线 | 亚洲人成网站精品片在线观看 | 亚洲在线黄色 | 国产精品自产拍在线观看蜜 | 天天干夜夜干 | 婷婷在线综合 | 欧美另类z0zx | 麻豆视屏 | 手机看片中文字幕 | 午夜天使| 99久久精品免费看国产一区二区三区 | 中文在线8新资源库 | 国产精品情侣视频 | 中文高清av | 中文字幕在线精品 | 最近日本中文字幕a | 亚洲精品国偷拍自产在线观看蜜桃 | 人人爽影院 | 日韩精品欧美专区 | 天天草天天操 | www.黄色片.com| 人人爽人人爽 | 玖玖在线播放 | 最新av网址在线观看 | 婷婷丁香六月 | 久久任你操 | 中文字幕免费高清在线观看 | 免费观看91| 蜜臀av一区| 国产精品久久久久免费观看 | 成人一级片视频 | 五月婷婷丁香色 | 久久国内免费视频 | 国产成人a亚洲精品 | 丁香六月久久综合狠狠色 | 99精品视频在线观看视频 | 久久天堂精品视频 | 免费日韩 精品中文字幕视频在线 | 国产中文字幕视频 | 日本久久中文 | 18久久久| 国产精品美女在线观看 | 色综合天天色综合 | 欧美性天天 | 久精品视频免费观看2 | 亚洲开心色 | 综合色狠狠 | 免费观看日韩av | 国产无吗一区二区三区在线欢 | 四虎5151久久欧美毛片 | 久久午夜精品视频 | 黄色日视频 | 久久久久久久免费 | 国产在线观看91 | 日本最大色倩网站www | www久草| 免费毛片一区二区三区久久久 | 日韩午夜大片 | 日日日干 | 国产无吗一区二区三区在线欢 | 激情影音 | japanesexxxxfreehd乱熟| 狠狠色丁香婷婷综合视频 | 国产原创91 | 国产精品99久久99久久久二8 | 九九九九色 | 色视频网站在线观看一=区 a视频免费在线观看 | 色婷婷亚洲 | 日韩精品中文字幕av | 日韩二区三区在线 | 亚洲成人免费 | 五月天丁香视频 | 欧美成人69av | 国产黄色片久久久 | 天天干天天射天天插 | 国产精品永久久久久久久www | 国产理论免费 | 国产精品久久网 | 在线免费观看欧美日韩 | 亚洲精品视频在线免费播放 | 亚洲精品国产精品国自产在线 | 国产中文欧美日韩在线 | 国产精品午夜av | 四虎最新入口 | 久久人91精品久久久久久不卡 | 99精品在线视频播放 | 色综合久久久久久中文网 | 手机av观看 | japanesefreesex中国少妇 | 92国产精品久久久久首页 | 干干夜夜 | 97超碰总站 | 成人免费视频免费观看 | 爱爱av网站 | 午夜精品久久久久久久久久久久久久 | 叶爱av在线| 五月婷婷综合激情网 | 国产精品一区二区免费 | 1000部18岁以下禁看视频 | 黄色日本免费 | 亚洲国产中文在线 | 亚洲精品网站在线 | 91色偷偷 | 一本一本久久a久久精品综合妖精 | 国产婷婷色 | 日本中文字幕在线 | 日韩中文字幕免费视频 | 久久精品视频3 | 亚洲精品玖玖玖av在线看 | 另类五月激情 | 免费网站在线观看人 | 一个色综合网站 | 中文字幕一区在线观看视频 | 久久精品综合一区 | 毛片激情永久免费 | 午夜精品久久久久久99热明星 | 夜夜操天天 | 欧美电影黄色 | 午夜影院在线观看18 | 中文字幕精品一区二区三区电影 | 在线视频观看你懂的 | 懂色av懂色av粉嫩av分享吧 | 五月婷婷丁香综合 | 人人爽人人av | 久草在线免费看视频 | 涩涩网站在线 | 成年在线观看 | 99热.com | 国产成人免费观看久久久 | 91精品少妇偷拍99 | 久久99深爱久久99精品 | 国产在线不卡视频 | 亚洲成人免费在线 | 黄色的视频网站 | 国产91免费在线观看 | 色中色亚洲 | 高清不卡一区二区三区 | 婷婷免费视频 | 国产黄色成人av | 免费福利在线视频 | 色噜噜日韩精品欧美一区二区 | 狠狠干网 | 波多野结衣视频一区 |