資訊:OLE Thread 模式的說明和作業方式

文章編號: 150777 - 檢視此文章適用的產品。
本文曾發行於 CHT150777
全部展開 | 全部摺疊

在此頁中

結論

COM 物件可以用於某個處理的多執行緒 (Multiple Threads)。「單執行緒公寓」(Single-threaded Apartment,STA) 和「多執行緒公寓」(Mutli-threaded Apartment,MTA) 詞彙, 用於建立一概念框架,來描述物件與 Thread 間的關係、物件間的並行關係、呼叫傳送給物件的 方法所使用的手段,以及在 Thread 之間傳送介面指標的規則。元件及其用戶端可於 COM 目前支 援的下列兩種 Apartment 模式之間做出選擇:

  1. 單執行緒公寓模式 (STA):處理過程中的一個或多個 Thread 使用 COM,且 COM 物件的呼叫由 COM 進 行同步化。Thread 之間針對介面進行編排。單執行緒公寓模式的退化情況 (其中,在既定處理中只有 一個 Thread 使用 COM) 被稱為單執行緒模式。過去的 Microsoft 資訊與文件曾經將 STA 模式簡 稱為「Apartment model」。
  2. 多執行緒公寓模式 (MTA):一個或多個 Thread 使用 COM,並且由所有與 MTA 相關的 Thread,直接呼叫與 MTA 有關的 COM 物件,在呼叫者和物件之間未受系統程式碼的插入。由於多個同步用戶端可能或 多或少同時呼叫物件(同時在多個處理器系統上),所以物件必須自行同步其內部狀態。Thread 之間未編排介面。過去的 Microsoft 資訊與文件曾經將此模式稱為「Free-threaded model」。
  3. STA 模式和 MTA 模式皆可用於同一 Process。 有時這種模式稱為「混合模型」的處理。
MTA 模式在 NT 4.0 中首次介紹,在含有 DCOM95 的 Windows 95 中可以使用。 STA 模式存在於 Windows NT 3.51 和 Windows 95,乃至於 NT 4.0 和含有 DCOM95 的 Windows 95 中。

其他相關資訊

概述

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 值:

  • ThreadingModel 數值不存在:支援單執行緒模式。
  • ThreadingModel=Apartment:支援 STA 模式。
  • ThreadingModel=Both:支援 STA 和 MTA 模式。
  • ThreadingModel=Free:僅支援 MTA 模式。
注意 :ThreadingModel 是一個命名值,而不是在 Win32 早期版本文件中錯誤歸檔的 InprocServer32 的 次機碼。

處理中伺服器的 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 模式:

  1. STA 模式伺服器:

    伺服器在一個或多個 STA 中進行 COM 作業。傳入的呼叫是由 COM 進行同步處理, 並由建立物件的 STA 相關 Thread 進行傳送。等級工廠方法呼叫是透過登錄等級工廠 的 STA 相關 Thread 進行傳送的。物件和等級工廠不需要實行同步作業。但是,實行 工具必須針多重 STA 使用的整體變數存取進行同步處理。伺服器必須使用 CoMarshalInterThreadInterfaceInStream 和 CoGetInterfaceAndReleaseStream 介面指標(可能來自其它伺服器)在 STA 間進行編排。 伺服器可以有選擇地在每個 STA 中實行 IMessageFilter,以控制由 COM 呼叫傳送的 行為。退化的情況是在一個 STA 中進行 COM 作業的單 Thread 模式伺服器。
  2. MTA 模式伺服器:

    伺服器在一個或多個 Thread 上進行 COM 作業,所有類似 Thread 均屬於 MTA。呼叫 未經 COM 進行同步處理。COM 在伺服器處理中建立 Thread 集區,並由這些 Thread 中的任意 Thread 傳送用戶端呼叫。Thread 無須復原和分派訊息。 物件和等級工廠必須實行同步處理。伺服器不需要對 Thread 之間的介面指標進行編組。
  3. 混合模型伺服器:

    有關詳細資訊,請參閱本文中「混合 Thread 模式」標題下的內容。

用戶端的 Thread 模式

有三種用戶端類型:

  1. STA 模式用戶端:

    用戶端在與一個或多個 STA 相關 Thread 中,進行 COM 作業。 用戶端必須使用 CoMarshalInterThreadInterfaceInStream 和 CoGetInterfaceAndReleaseStream 對 STA 之間的介面指標進行編排。退化情況是在一個 STA 中進行 COM 作業的單 Thread 模式用戶端。 用戶端的 Thread 在進行傳出呼叫時,進入一個 COM 所提供的訊息迴圈。在等待呼叫結束和其它並行 問題的同時,用戶端可以使用 IMessageFilter 來管理視窗訊息的叫回和處理。
  2. MTA 模式用戶端:

    用戶端在一個或多個 Thread 中進行 COM 作業,所有此類 Thread 均屬於 MTA。用戶端無須針對 Thread 之間的介面指標進行編排。用戶端無法使用 IMessageFilter。為處理外物件進行 COM 呼叫時,客戶的 Thread 暫時停止,並於該呼叫傳回時繼續。傳入的呼叫到達 COM 所建立與管理 的 Thread 上。
  3. 混合模式用戶端:

    有關詳細資訊,請參閱本文中標示為「混合 Thread 模式」的內容。

處理(以 DLL 為基礎) 伺服器中的 Thread 模式:

以下是四種類型的處理中伺服器,每種皆可由任何 COM 用戶端使用,而不論該用戶端使用 何種 Thread 模式。但是,如果欲支援 Thread 模式的跨平台作業,處理中伺服器必須為其實行 的任何自訂(非系統定義)介面提供編排程式碼。這是因為這種情況通常需要對用戶端 Apartment 之間的介面進行編排。這四種類型分別為:

  1. 支援單 Thread(「主要」STA)的處理中伺服器 - 無 ThreadingModel 值:

    由這種伺服器提供的物件,應該由建立的用戶端 STA 進行存取。此外,伺服器期望其所有的進入點,如 llGetClassObject 和 DllCanUnloadNow 以及整體資料,皆由同一執行緒(與主要 STA 相關的)存取。 在 COM 中匯入多 Thread 之前存在的伺服器屬於這種類別。由於這些伺服器不是為多 Thread 存取而設計, 以 COM 建立由伺服器處理的主要 STA 中提供的所有物件,並且物件呼叫是透過與主要 STA 相關的 Thread 來傳送。其它用戶端 Apartment 透過 psoxy 取得物件的存取權。來自其它 Apartment 的呼叫,從代理人進入主要STA 的 stub(Thread 之間的編排),然後再進入該物件。此編排啟動 COM 同步物件呼叫, 並且由其中建立該物件的 STA 傳送呼叫。由於 Thread 之間的編排與直接呼叫相較之下較為緩慢, 因此建議重新編寫這些伺服器,以便支援多個 STA(類型 2)。
  2. 支援單執行緒公寓模式(多重 STA )的處理中伺服器 - 標示為 ThreadingModel=Apartment:

    由這種伺服器提供的物件,應該由 STA 建立的用戶端 STA 進行存取。因此,它與單 Thread 的 處理中伺服器所提供的物件類似。但是,由於本伺服器提供的物件可以在處理的多個 STA 中 建立,因此該伺服器必須設計其進入點(如 DllGetClassObject 和 DllCanUnloadNow)以及 整體資料以供多 Thread 使用。例如,如果處理的兩個 STA 同步建立處理中物件的兩個實例, 則 DllGetClassObject 可能由兩個 STA 同時呼叫。同樣的,必須寫入 DllCanUnloadNow 以 防止程式碼正在伺服器上執行時,伺服器遭到卸載。

    如果伺服器只提供一個建立所有物件的等級工廠實例,則必須將等級工廠實行設計用於 多 Thread;這是因為它將自多用戶端 STA 進行存取。如果每次呼叫 DllGetClassObject 時, 伺服器都建立一個等級工廠的新實例,則等級工廠不要求具備 Thread 安全性。但是,實行工具 必須同步進行任何整體變數的存取。

    等級工廠建立的 COM 物件無須具備 Thread 安全性。但是,整體變數的存取必須由實行工具同步 進行。一旦由 Thread 建立物件,則物件永遠透過該 Thread 進行存取,並且所有物件呼叫由 COM 進行同步。和建立物件的 STA 不同的客戶 Apartment,必須透過代理人來存取物件。這些代理人是 在用戶端對其 Apartment 之間的介面,進行編排時所建立的。

    透過等級工廠建立 STA 物件的任何用戶端,皆取得一個指向該物件的直接指標。這與單執行 緒處理中物件有所不同;前者只有用戶端的主要 STA 取得指向物件的直接指標,且所有建立 物件的其它 STA 取得許可權,可透過代理人存取該物件。由於 Thread 之間的編排,與直接呼叫 相較而言較為緩慢,所以將單 Thread 處理中伺服器,變更為支援多 STA 的伺服器,即可顯著地 提高其速度。
  3. 只支援 MTA 的處理中伺服器 - 標示為 ThreadingModel=Free:

    由此伺服器提供的物件僅對於 MTA 而言較具安全性。它實行自身的同步化,並可由多用戶端 Thread 同時進行存取。此伺服器可能存在與 STA 模式不相容的行為。( 例如,依中斷 STA 訊息 幫浦的方式,使用視窗訊息佇列。)另外,將物件的 Thread 模式標示為「自由」(Free),物件的實行程式 按照以下所示進行:此物件可以由任何 Thread 進行呼叫,但也可以直接透過介面指標(不必編排), 傳送到任何其建立的 Thread,同時這些 Thread 可透過指標進行呼叫。因此,如果用戶端將指向用 戶端實行物件的介面指標(如水槽),傳送給此物件,即可選擇透過該介面指標,從任何其建立 的 Thread 進行叫回。如果用戶端是 STA,從某個與建立水槽物件不同的 Thread,進行直接呼叫時, 會出現錯誤(如上述 2 中所示)。因此,COM 永遠確保 STA 相關 Thread 的用戶端,僅透過代理人 對這類處理中物件進行存取。此外,這些物件不應與自由 Thread 編排程式進行集合,因此可允許其 在 STA Thread 上直接執行。
  4. 支援 Apartment 模型和自由 Thread 的處理中伺服器 - 標示為 ThreadingModel=Both:

    此伺服器所提供的物件,實現了其本身的同步化,並且可由多個用戶端 Apartment 並行存取。此外, 物件無須透過代理人,在 STA 或用戶端處理的 MTA 中,直接建立和使用。由於此物件在 STA 中 直接使用,因此伺服器必須對 Thread 之間物件(可能來自其它伺服器)的介面進行編排,以便按 Thread 適當的行為,對其它任何物件的存取有所保證。此外,透過將物件的執行緒模式標示為「雙方」 的行為,物件的實行程式如下所示:此物件可以從任何其它用戶端 Thread 進行呼叫,但任何從此物件 向用戶端發出的呼叫,僅於物件接收叫回物件介面指標的 Apartment 上進行。COM 允許在 STA 和客戶處理 的 MTA 中,直接建立這樣的物件。

    由於建立這種物件的任何 Apartment ,可永遠取得直接指標,而非代理人指標,因此 ThreadingModel 「雙方」(Both) 物件在 STA 中載入時,為 ThreadingModel「自由」(Free) 物件提供改良的效能。

    由於 ThreadingModel「雙方」物件也是針對 MTA 存取而設計(在內部具備完全 Thread 安全性), 因此可以透過 CoCreateFreeThreadedMarshaler 提供的編排,進行集合而加速性能。該系統所提供的 物件集入任何呼叫物件中,並將指向物件的自訂編排直接指標,集合到處理中的所有 Apartment。然後, 不論是 STA 還是 MTA,任何 Apartment 的用戶端都不必透過代理人即可直接存取物件。例如,STA 模式 用戶端在 STA1 中建立處理中物件,並將物件編排到 STA2 上。如果物件不與自由 Thread 編排程式 進行集合,則 STA2 取得許可權,可透過代理人存取物件。如果與自由執行緒編排程式進行集合, 則自由 Thread 編排程式,為 STA2 提供指向物件的直接指標。

    注意 :與自由 Thread 進行集合時,一定要格外注意。例如,假設標示有 ThreadingModel「雙方」(Both)(同時與 自由 Thread 編排程式進行集合)的物件有一資料成員,該成員是到達 ThreadingModel 為「Apartment」的 另一物件的介面指標。然後假設 STA 建立第一個物件,並且在建立程序的第一個物件中建立第二個物件。 根據上述規則,第一個物件現在有一個到第二個物件的直接指標。現在,假設 STA 將到第一個物件的介面 件與自由 Thread 編排程式進行集合,因此到第一個物件的直接指標,給予第二個 Apartment。如果第二個 Apartment 隨 後透過此指標進行呼叫,而且如果此引起第一個物件的呼叫,是透過到第二個物件的介面指標來完成, 則已經發生錯誤,因為第二個物件不可直接從第二個 Apartment 呼叫。如果第一個物件擁有透過代理人指向第 二個物件的指標,而不是一個直接指標,則會導致其它類型的錯誤。系統代理人也是一種 COM 物件, 僅與單一 Apartment 相關連。為了避免某些循環狀況發生,必須追蹤其 Apartment。所以,從與不同 Apartment 相關的代理人 上(而不是與該物件執行中的 Thread 上)調出物件,將來自該 proxy 的 RPC_E_WRONG_THREAD 傳回,則 該呼叫會失敗。

用戶端與處理中物件之間的 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 的明確定義,在下列兩個步驟中說明:

  1. 如果包含處理中伺服器類別的 DLL,過去未透過作業系統載入程式進行載入(映射到處理位址空間), 則該作業已執行,且 COM 取得 DLL 輸出的 DLLGetClassObject 函數位址。如果 DLL 過去已經由 任何 Apartment 相關的 Thread 載入,請跳過本步驟。
  2. COM 使用與「載入」Apartment 相關的 Thread(或者在 MTA 情況下,是單一 Thread),尋找由 DLL 所需類別 的 CLSID,來呼叫匯出的 DllGetClassObject 函數。然後,傳回的工廠物件用來建立該類物件 的實例。

    每當一個客戶呼叫 CoGetClassObject/CoCreateIntance[Ex] 時,即使是從同一 Apartment 內,也會產生 第二個步驟(由 COM 呼叫 DllGetClassObject)。換言之,DllGetClassObject 可以由同 Apartment 相關 的 Thread 呼叫多次;這完全取決於該 Apartment 有多少個用戶端,正在試圖為該類別取得類別工廠物件的 存取許可權。
最終,類別實行的編寫者,和既定類別的 DLL 封包編寫者,擁有完整的自由權, 可依 DllGetClassObject 函數呼叫,決定傳回何種工廠物件。因為在單 DLL 範圍的 DllGetClassObject() 進入點「後方」的程式碼,擁有最優先和最終決定許可權,所以 DLL 封包 的編寫者擁有最終決定權。三種典型的可能性分別為:

  1. DllGetClassObject 根據呼叫 Thread,傳回唯一類別工廠物件(表示一個 STA 對應一個類別工廠物件, 且於潛在情況下在 MTA 內對應多個類別工廠)。
  2. DllGetClassObject 不管以何種身份呼叫 Thread,或者以何種呼叫 Thread 的相關 Apartment,永遠傳回 相同類別的工廠物件。
  3. DllGetClassObject 根據呼叫 Apartment,傳回唯一的類別工廠物件(在 STA 和 MTA 中,一個 Apartment 對應一個)。
DllGetClassObject 呼叫與實際傳回的類別工廠物件之間,還有其它可能存在的關係 (如針對 DllGetClassObject 呼叫建立新的類別工廠物件),但是目前似乎不是非常有用。

處理中伺服器的用戶端與物件 Thread 模式總結

下表彙總了用戶 Thread 在處理中伺服器實行類別中,首次呼叫 CoGetClassObject 時, 不同 Thread 模式之間的交互作用。

用戶端/Thread 的類型:

  • 用戶端在一個與「主要」STA相關的 Thread(用 COINIT_APARTMENTTHREADED 標示 呼叫 CoInitialize 或 CoInitializeEx 的第一個 Thread)中執行 - 稱為 STA0 (亦稱為單 Thread 模式)。
  • 用戶端在某個與任何其它 STA [ASCII 150] 相關的 Thread 中執行,稱為 STA*。
  • 用戶端在某個與 MTA 相關的 Thread 中執行。
DLL 伺服器的類型:

  • 伺服器沒有 ThreadingModel 機碼 -- 稱為"無"。
  • 伺服器標示為 Apartment -- 稱為「Apartment」。
  • 伺服器標示為 Free「自由」。
  • 伺服器標示為 Both「雙方」。
檢視下表時,請記住上述有關將伺服器「載入」某 Apartment 中的定義。
用戶端    伺服器      結果
------     ------     -----------------------------------------
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 OLE Thread 必須傳送訊息
137629 在 Apartment 模式用戶端的處理中物件自訂介面

屬性

文章編號: 150777 - 上次校閱: 2004年9月22日 - 版次: 1.2
這篇文章中的資訊適用於:
  • Microsoft OLE 4.0
關鍵字:?
letwoarc letwocom kbhowto kbhowto KB150777
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。
依現狀不再更新的知識庫內容免責聲明
本文旨在說明 Microsoft 不再提供支援的產品。因此,本文係依「現狀」提供,不會再更新。

提供意見