Select the product you need help with
資訊:OLE Thread 模式的說明和作業方式文章編號: 150777 - 檢視此文章適用的產品。 本文曾發行於 CHT150777 在此頁中結論
COM 物件可以用於某個處理的多執行緒 (Multiple Threads)。「單執行緒公寓」(Single-threaded Apartment,STA) 和「多執行緒公寓」(Mutli-threaded Apartment,MTA) 詞彙,
用於建立一概念框架,來描述物件與 Thread 間的關係、物件間的並行關係、呼叫傳送給物件的
方法所使用的手段,以及在 Thread 之間傳送介面指標的規則。元件及其用戶端可於 COM 目前支
援的下列兩種 Apartment 模式之間做出選擇:
其他相關資訊概述COM 中的 Thread 模式為使用不同 Thread 結構的元件,提供共同作業的機制。同時也為需要的元件 提供同步服務。例如,某特定物件可能設計僅限由單 Thread 呼叫,並可能無法將來自用戶端的並 行呼叫同步處理。如果這種物件由多 Thread 同時呼叫,該物件會失敗或產生錯誤。COM 提供處理 這種 Thread 跨平台結構處理的機制。即使支援 Thread 的元件也常常需要同步服務。例如,OLE/ActiveX 控制、原有範疇內的作用 嵌入項、ActiveX 文件等具有圖形使用者介面 (GUI) 的元件,需要對 COM 呼叫和視窗資訊 進行同步和串列。COM 提供這些同步操作服務,所以編寫元件時不需要複雜的同步程式碼。 「Apartment」有幾個相互關連的關點。首先從並行的角度來說,它是一個邏輯結構,例如 Thread 與一系列 COM 物件的相關性。其次,它是程式編寫人員必須遵守的一套規則,以便在 COM 環境下實現期望的並行列為。最後,它是系統提供的程式碼,可用來協助程式員以並行方式 管理 COM 物件的 Thread。 「Apartment」這個詞彙來自一個比喻,處理程序在其中想像為完全分離的實體,就好像一座大廈被 分為一系列相關而又不同的「地區」,並稱之為「Apartment」一樣。Apartment 是一個「邏輯容器」,在物 件之間(某些情況下在 Thread 之間)建立關連。雖然 STA 模式中可能只有單一 Thread 與某 Apartment 有邏輯上的關聯,但 Thread 並非 Apartment。 雖然每個物件僅與單一 Apartment 相關連,物件並不是 Apartment。但是,Apartment 不僅是一個邏輯結構;其規則 說明了 COM 系統的行為。如果不遵守 Apartment 模式的規則,COM 物件將無法正常運作。 詳細內容單執行緒公寓 (STA) 是一系列與特定 Thread 相關的 COM 物件。這些物件透過由該 Thread 建立, 或者更精確地說,率先在該 Thread 上公開給 COM 系統(典型透過編排)因而與 Apartment 相關連。 STA 視為物件或代理人 (Proxy)「生存」的地方。如果物件或代理人需要由另一 Apartment 進行存取(在相同 或不同的處理中),其介面指標必須編排至該 Apartment 中,並建立新的代理人。若允許使用該 Apartment 模 式規則,則在相同處理中不允許從其它 Thread 直接呼叫該物件;這樣會違反在既定 Apartment 內所有物 件,都應該於單一 Thread 上執行的規則。此規則的目的,是因為如果在其它執行緒上正常運作,多 數在 STA 下執行的程式碼將無法正常執行。與 STA 相關的 Thread 必須呼叫 CoInitialize 或 CoInitializeEx (NULL, COINIT_APARTMENTTHREADED), 而且必須為相關的物件,恢復並分派視窗訊息,以接收傳入的呼叫。如本文後面所述,COM 使用 視窗訊息向 STA 中的物件分派和同步呼叫。 「主要 STA」是在既定處理中首先呼叫 CoInitialize 或 CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) 的 Thread。 如本文後面所述,由於某些處理內的物件,永遠在主要 STA 中載入,所以處理的主要 STA 必須永遠保持有效,直到所有 COM 運作完成為止。 Windows NT 4.0 和 DCOM95 引進了一種新型 Apartment ,即多執行緒公寓 (MTA)。MTA 是與處理中 一組 Thread 相關的某組 COM 物件,以便所有 Thread 皆可直接呼叫任何物件執行,而無須系統 程式碼的介入。 指向 MTA 中任何物件的介面指標,可以不必經過編組而在與 MTA 相關的執行緒間進行傳送。 處理中呼叫 CoInitializeEx(NULL, COINIT_MULTITHREADED) 的所有 Thread 皆與 MTA 相關。 與上述 STA 不同的是,MTA 的 Thread 不需要為相關物件恢復和分派視窗訊息,才能接收傳 入的呼叫。COM 不針對 MTA 的物件呼叫進行同步。MTA 的物件必須自行保護其內部狀態, 以防止多個同步 Thread 的交互作用而受損,且這些物件以無法假設不同的方法呼叫時,Thread-Local Storage (TLS) 的內容保持不變。 一項處理可以有多個 STA,但最多只能有一個 MTA。MTA 包含一或多個 Thread。每個 STA 只有 一個 Thread 。一個 Thread 最多只能屬於單一 Apartment。物件只能屬於一個 Apartment。介面指標應該永遠 在 Apartment 之間進行編排(雖然編排的結果可能是直接指標,而非代理人)。請參閱以下有關 CoCreateFreeThreadedMarshaler 的資訊。 一項處理從 COM 所提供的 Thread 模式中選擇一種。STA 模式處理含有一或多個 STA,且沒有 MTA。 MTA 模式處理含有一或多個 Thread 的單一 MTA,且沒有任何 STA。混合模式處理含有一個 MTA 和 任何數量的 STA。 單執行緒公寓模式STA 的 Thread 必須呼叫 CoInitialize 或 CoInitializeEx (NULL, COINIT_APARTMENTTHREADED), 且必須恢復和分派視窗訊息。這是因為 COM 使用視窗訊息來恢復和分派本模式中的物件呼叫傳送。 請參閱以下的「參考文章」部分,以取得更多資訊。支援 STA 模式的伺服器: 在 STA 模式中,COM 同步對物件的呼叫,正如視窗訊息同步傳送到視窗一樣。使用視窗訊息 將呼叫傳送到建立該物件的 Thread 上。 因此,該物件的 Thread 必須呼叫 Get/PeekMessage 和 DispatchMessage 以接收呼叫。COM 為 每個 STA 建立一相關的隱藏視窗。STA 外的物件呼叫是透過視窗訊息張貼至隱藏視窗,從 COM 執行階段傳輸到該物件 Thread。與物件的 STA 相關的 Thread 恢復並分派訊息、隱藏視窗的視窗 程序(也由 COM 實行)接收該訊息。由於 COM 執行階段同時位於從 COM 所屬 Thread 到 STA Thread 呼叫的兩面,所以 COM Thread 段可用視窗程序來「攔截」與 STA 相關的 Thread。COM 執行 階段(目前在 STA Thread 上執行)透過 COM 提供的 stub,「向上」呼叫對應的物件介面方法。 以該方式呼叫傳回時,利用剛才的呼叫執行路徑反向退回;該呼叫傳下至 stub 和 COM 執行階 段,然後透過視窗訊息傳送控制到 COM 執行階段 Thread 中,然後通過 COM 管道傳回到原始呼叫者。 當多個客戶呼叫 STA 物件時,這些呼叫透過在 STA 所使用的控制傳輸機制,自動排列在訊息佇 列中。每次當物件的 STA 恢復和分派訊息時,物件都會接到一個呼叫。由於使用此方法由 COM 進行同步呼叫,且由於呼叫永遠在傳送到與物件的 STA 相關的單 Thread 上,所以物件的介面實行 無須提供同步。 注意 : 如果介面方法在處理某方法呼叫時,恢復並分派訊息,從而引起另一個呼叫自相同的 STA 傳送 到該物件,則該物件已經重新輸入了。發生這種情況的常見原因是,如果 STA 物件使用 COM 進 行向外(跨公寓/跨處理)呼叫。這種情況與視窗程序在處理訊息的同時又恢復並分派消息,引 起視窗程序重新輸入的情形完全相同。雖然 COM 無法防止在相同 Thread 上重新輸入,但它確實 防止並行的執行。此方法亦提供 COM 相關的重新輸入問題管理方式。請參閱見以下的「參考文章」 部分以取得更多資訊。如果方法實行時,未呼叫其 Apartment,亦無法恢復和分派訊息,則物件不會重新輸入。 STA 模式中的用戶端責任: 使用 STA 模式的處理和/或 Thread 中執行的用戶端程式碼,必須使用 CoMarshalInterThreadInterfaceInStream 和 CoGetInterfaceAndReleaseStream, 對 Apartment 之間的物件介面進行編排。 例如,如果用戶端的 Apartment 1 有介面指標,而 Apartment 2 需要使用,則 Apartment 1 必須用 CoMarshalInterThreadInterfaceInStream 對該介面進行編排。由此函數傳回的串流 物件屬於安全 Thread,且其介面指標應儲存於可由 Apartment 2 存取的直接記憶體變數中。 Apartment 2 必須將串流物件介面,傳送給 CoGetInterfaceAndReleaseStream,以對其下 的物件介面取消編排,並傳回一個指向代理人的指標,透過它來直接存取物件。 由於某些處理階段物件已載入主要 Apartment,所以在用戶端完成所有的 COM 作業之前, 特定處理的主要 Apartment 必須始終保持有效。(詳細資訊闡述如下)。 多執行緒公寓模式MTA 是由已呼叫 CoInitializeEx(NULL, COINIT_MULTITHREADED) 的程序中,所有 Thread 建立或公開的物件集合。注意 :COM 的目前實行,允許將未明確起始化 COM 的 Thread,作為 MTA 的一部分。 只有在處理中至少有一個 Thread 曾呼叫 CoInitializeEx(NULL, COINIT_MULTITHREADED) 之後, 才開始使用 COM 的情況下,未起始化 COM 的 Thread 才能作為 MTA 的一部分。 (若客戶 Thread 未曾明確起始化 MTA,COM 甚至有可能自己將 MTA 起始化。例如,與 STA 相關的 Thread 在標示為 ThreadingModel=Free 的 CLSID 上, 呼叫 CoGetClassObject/CoCreateInstance[Ex],COM 暗中建立將類別物件載入其中的 MTA。) 請參閱以下有關 Thread 模式的跨平台作業。 但這是一個可能造成問題的組態,例如某些情況下的違規存取。因此,強烈建議每個需要 進行 COM 作業的 Thread,皆透過呼叫 CoInitializeEx 對 COM 進行起始化,然後在 COM 作業完成時呼叫 CoUninitialize。「多餘的」MTA 起始化程序所需的成本很小。 由於 COM 在此模式中未使用視窗訊息傳送物件呼叫,所以 MTA Thread 不需要恢復和分派訊息。 支援 MTA 模式的伺服器: 在 MTA 模式中,物件呼叫沒有透過 COM 進行同步。多個用戶端可以在不同的 Thread 上, 並行呼叫支援此模式的物件,且物件必須在其介面/方法實行過程中使用諸如事件、mutex、號誌 等同步物件來提供同步作業。MTA 物件可以透過屬於該物件處理的 COM 所建立的 Thread 集區, 接收來自多個處理外用戶端的並行呼叫。MTA 物件可以從 MTA 相關的多 Thread 上的多個處理中用 戶端,接收並行呼叫。 MTA 模式中的用戶端責任: 在使用 MTA 模式的處理和/或 Thread 上執行的用戶端程式碼,無須在其本身和其它 MTA Thread 之間,針對物件的介面指標進行列編排。反之,一個 MTA Thread 可以使用來自另一個 MTA 執行 緒的介面指標,作為直接記憶體指標。用戶端 Thread 呼叫處理程序之外的物件時,處於暫停狀態 直到該呼叫完成為止。所有與 MTA 相關的應用程式建立的 Thread,在傳出呼叫上受到阻擋時,呼 叫可能會到達與 MTA 相關的物件。在這種情況及一般情況下,傳入的呼叫會傳送到 COM 執行階段 所提供的 Thread 上。在 MTA 模式中未提供訊息篩選器 (IMessageFilter) 供您使用。 混合執行緒模式支援混合 Thread 模式的處理,會使用單一 MTA 以及一個或多個 STA。介面指標必須在所有 Apartment 之間進行編排,但在 MTA 內可以不經過編排即使用。STA 中的物件呼叫由 COM 同步於單一執行 緒上執行,而在 MTA 中對物件的呼叫則非如此。但是,STA 向 MTA 的呼叫通常經過系統提供的 程式碼,並於傳送到物件之前,由 STA Thread 切換到 MTA Thread 。注意 :有關在何處可使用直接指標、STA Thread 如何直接呼叫至 MTA 相關的首要物件,以及反向從 多 Apartment 呼叫 STA 的相關資訊,請參閱 SDK 文件上有關 CoCreateFreeThreadedMarshaler() 的 內容以及下列關於該 API 的討論。 選擇執行緒模式元件可以選擇支援 STA 模式、MTA 模式或混合執行緒模式。例如,執行大量 I/O 的物件 可以選擇支援 MTA,這樣便可於 I/O 等待時間,進行介面呼叫,為用戶端提供最多的回應。 或者,與使用者相互作用的物件幾乎永遠選擇支援 STA,以便使用 GUI 操作同步處理傳入 的 COM。由於 COM 提供同步作業,因此支援 STA 模式較為容易。由於物件必須實行同步 處理,因此支援 MTA 模式較為困難,但因為同步僅用於小部分程式碼,而不是用在由 COM 所 提供的整體介面呼叫,所以回應用戶端較好。由於 STA 模式亦用於 Microsoft Transaction Server(MTS,以前的程式碼名為 Viper), 因此計畫在 MTS 環境中執行的 DLL 基礎物件,應該採用 STA 模式。為 MTA 模式實行的物件, 通常在 MTS 環境中可以正常作業。但是,由於使用多餘的 Thread 同步還原,所以無法有效地執行。 標示處理中伺服器的支援 Thread 模式如果 Thread 呼叫了 CoInitializeEx(NULL, COINIT_MULTITHREADED),或者未起始化即使用 COM, 則使用 MTA 模式。如果 Thread 呼叫 CoInitialize 或 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED),則使用 STA 模式。由於 COM 執行階段的啟動程式碼,可依所需的型式起始化 COM,因此 CoInitialize API 為 .EXE 內的使用者程式碼和物件,提供 Apartment 控制。 但是,由於這些 API 在載入之前已經被呼叫,因此處理中(以 DLL 為基礎)COM 伺服器 不呼叫 CoInitialize/CoInitializeEx。因此,DLL 伺服器必須使用登錄來通知 COM 其所 支援的 Thread 模式,以便 COM 可以保證系統依某種與之相容的行為執行。為此,請依以下 行為使用元件的 CLSID\InprocServer32 識別碼的 ThreadingModel 值:
處理中伺服器的 Thread 模式將在本文後面的部份進行討論。如果處理中伺服器提供多種物件 類型(每種類型都有自己唯一的 CLSID),則每種類型皆可擁有不同的 ThreadingModel 值。 換言之,Thread 模式是依據 CLSID,而非根據程式碼封包/DLL。但是,啟動和查詢所有處理中 伺服器必要的 API 進入點 (DLLGetClassObject(),DLLCanUnloadNow()) ,對於任何支援多 Thread(即 Apartment 的 ThreadingModel 值為 Apartment,Both 或 Free)的處理中伺服器而言, 必須保持 Thread 安全性。 如前所述,處理外伺服器未使用 ThreadingModel 對本身進行標示,而是使用 CoInitialize 或 CoInitializeEx。預期使用 COM 「代理人」(surrogate) 功能 如系統提供的代理人 DLLHOST.EXE)來執行處理外程序的 DLL 基礎伺服器,只要 遵循以 DLL 為基礎的伺服器規則即可;在這種情況下,不需要有任何特殊考量。 用戶端和物件使用不同的 Thread 模式時即使由於用戶端和物件位於不同程序,而使用相異的 Thread 模式,且 COM 涉及從用戶端 向物件傳送呼叫的情況下,用戶端和處理外物件之間的交互作用,也是非常直接的。 由於 COM 介入用戶端與伺服器之間,因此為 Thread 模式之間的跨平台作業提供程式碼。 例如,如果 STA 物件由多個 STA 或 MTA 用戶端並行呼叫,COM 將於伺服器訊息佇列中 放置相對的訊息視窗,來將這些呼叫同步處理。每次物件的 STA 進行復原和分派訊息時, 都會接到一個呼叫。Thread 模式跨平台作業的所有組合,皆可於用戶端和處理外物件之間, 進行並獲得全面支援。用戶端與使用不同 Thread 模式的處理內物件之間的交互作用較為複雜。雖然伺服器在處理中, 但有時 COM 必須讓本身介入用戶端和物件之間。例如,應該支援 STA 模型的處理內物件, 可能會由用戶端的多 Thread 並行呼叫。由於該物件不是專為這種並行存取而設計,因此 COM 不 允許用戶端 Thread 直接存取物件的介面。反之,COM 必須確保呼叫已經同步,並且只能由與「包含」 該物件的 STA 相關 Thread 進行呼叫。儘管存在這些附加的複雜性,仍然允許在用戶端與處理中 物件之間,進行所有 Thread 模式跨平台作業的組合。 處理外(以 EXE 為基礎)伺服器中的 Thread 模式以下是處理外伺服器的三種類型,每一種皆可由任何 COM 用戶端使用,不論該客戶使用的是哪種 Thread 模式:
用戶端的 Thread 模式有三種用戶端類型:
處理(以 DLL 為基礎) 伺服器中的 Thread 模式:以下是四種類型的處理中伺服器,每種皆可由任何 COM 用戶端使用,而不論該用戶端使用 何種 Thread 模式。但是,如果欲支援 Thread 模式的跨平台作業,處理中伺服器必須為其實行 的任何自訂(非系統定義)介面提供編排程式碼。這是因為這種情況通常需要對用戶端 Apartment 之間的介面進行編排。這四種類型分別為:
用戶端與處理中物件之間的 Thread 模式跨平台作業用戶端和處理中物件之間,允許任何 Thread 模式的跨平台作業組合。COM 允許所有 STA 模式用戶端與單 Thread 處理中物件進行交互操作,這樣可以透過建立並 存取用戶端的主要 STA 物件,將其編排到呼叫 CoCreateInstance[Ex] 的用戶端 STA 來實行。 如果用戶端的 MTA 在處理伺服器中建立了一個 STA 模式,則 COM 在用戶端中加入「主機」STA。 此主機 STA 可建立物件,且介面指標會編排回 MTA。同樣的,當 STA 建立一處理中 MTA 伺服器時, COM 加上一個主機 MTA,在其中建立物件並將其編排回 STA。由於單 Thread 模式僅屬於 STA 模式 的退化情況,所以單 Thread 模式與 MTA 模式之間的跨平台作業,也進行了類似的處理。 對於任何處理中伺服器實行的自訂介面,如果要支援的跨平台作業,需要 COM 對用戶端 Apartment 之間的 介面進行編排,則必須提供編排程式碼。有關詳細資訊,請參閱以下「參考文章」部份。 Thread 模式與傳回的等級工廠物件之間的關係處理中伺服器「載入到」 Apartment 的明確定義,在下列兩個步驟中說明:
處理中伺服器的用戶端與物件 Thread 模式總結下表彙總了用戶 Thread 在處理中伺服器實行類別中,首次呼叫 CoGetClassObject 時, 不同 Thread 模式之間的交互作用。用戶端/Thread 的類型:
用戶端 伺服器 結果
------ ------ -----------------------------------------
STA0 無 直接存取; 伺服器載入到 STA0
STA* 無 代理人存取; 伺服器載入到STA0。
MTA 無 代理人存取; 伺服器載入到 STA0; 如果必要 STA0 由 COM 自動建立
:
STA0 Apartment 直接存取; 伺服器載入到 STA0
STA* Apartment 直接存取; 伺服器載入到 STA*
MTA Apartment 代理人存取; 伺服器載入到一個
由 COM 自動建立的 STA。
STA0 自由 代理人存取; 伺服器載入到 MTA
如果必要 MTA 由 COM 自動建立。
STA* 自由 與 STA0->自由相同
MTA 自由 直接存取
STA0 雙方 直接存取; 伺服器載入到 STA0
STA* 雙方 直接存取; 伺服器載入到 STA*
MTA 雙方 直接存取; 伺服器載入到 MTA
?考
在 CoRegisterMessageFilter() 和 IMessageFilterSDK 介面上的 SDK 文件。
有關詳細資訊,請參閱 Microsoft Knowledge Base 的以下文件: 136885
(http://support.microsoft.com/kb/136885/EN-US/
)
OLE Thread 必須傳送訊息
137629
(http://support.microsoft.com/kb/137629/EN-US/
)
在 Apartment 模式用戶端的處理中物件自訂介面
屬性文章編號: 150777 - 上次校閱: 2004年9月22日 - 版次: 1.2
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。 | 文章翻譯
|



回此頁最上方








