單個線程 Apartment

使用單個線程 Apartment (Apartment 模型進程) 提供訊息型範例,以處理同時執行的多個物件。 它可讓您撰寫更有效率的程式碼,方法是允許線程,同時等候一些耗時的作業完成,以允許執行另一個線程。

初始化為 Apartment 模型進程且擷取和分派視窗訊息的進程中的每個線程都是單個線程 Apartment 線程。 每個線程都位於它自己的 Apartment 內。 在 Apartment 中,介面指標可以傳遞而不進行封送處理,因此,單一線程 Apartment 線程中的所有對象都會直接通訊。

所有在相同線程上執行之相關對象的邏輯群組,因此必須有同步執行,可以存在於相同的單個線程 Apartment 線程上。 不過,Apartment 模型物件不能位於一個以上的線程上。 其他線程中的物件呼叫必須在擁有線程的內容中進行,因此分散式 COM 會在您在 Proxy 上呼叫時自動切換線程。

進程間和線程間模型很類似。 當必須在相同進程中將介面指標傳遞至另一個 Apartment 中某個物件(在另一個線程上)時,您會使用相同的封送處理模型,讓不同進程中的物件用來跨進程界限傳遞指標。 藉由取得標準封送處理物件的指標,您可以透過進程之間的相同方式,跨線程界限(Apartments 之間)封送處理介面指標。 (介面指標必須在 Apartment 之間傳遞時封送處理。

單個線程 Apartment 的規則很簡單,但請務必仔細遵循這些規則:

  • 每個物件都應該只存在於一個線程上(在單個線程 Apartment 內)。
  • 初始化每個線程的 COM 連結庫。
  • 在 Apartment 之間傳遞物件時,封送處理物件的所有指標。
  • 每個單個線程 Apartment 都必須有一個訊息迴圈,才能處理相同進程內其他進程和 Apartment 的呼叫。 不含物件的單線程 Apartment(僅限用戶端)也需要訊息循環來分派某些應用程式使用的廣播訊息。
  • DLL 型或進程內物件不會呼叫 COM 初始化函式;相反地,他們會使用登錄中 InprocServer32 機碼下的 ThreadingModel 具名值來註冊其線程模型。 Apartment 感知物件也必須小心寫入 DLL 進入點。 有適用於進程內線程伺服器的特殊考慮。 如需詳細資訊,請參閱 進程伺服器線程問題

雖然多個物件可以存在於單一線程上,但任何 Apartment 模型物件都不能存在於多個線程上。

客戶端進程或跨進程伺服器的每個線程都必須呼叫 CoInitialize,或呼叫 CoInitializeEx,併為 dwCoInit 參數指定COINIT_APARTMENTTHREADED。 主要 Apartment 是先呼叫 CoInitializeEx 的線程。 如需進程內伺服器的資訊,請參閱 進程伺服器線程問題

物件的所有呼叫都必須在其線程上進行(在其 Apartment 內)。 禁止直接從另一個線程呼叫 物件;以這個自由線程方式使用 物件可能會導致應用程式發生問題。 此規則的含意在於,在 Apartment 之間傳遞時,物件的所有指標都必須封送處理。 COM 針對此目的提供下列兩個函式:

這些函式會包裝對 CoMarshalInterface 和 CoUnmarshalInterface 函式的呼叫,這需要使用 MSHCTX_INPROC 旗標。

一般而言,封送處理是由 COM 自動完成。 例如,將介面指標當做方法呼叫中的參數傳遞至另一個 Apartment 中的物件時,或呼叫 CoCreateInstance 時,COM 會自動執行封送處理。 不過,在某些特殊情況下,應用程式寫入器會在 Apartment 之間傳遞介面指標,而不使用一般 COM 機制,寫入器必須手動處理封送處理。

如果進程中的一個 Apartment (Apartment 1) 具有介面指標,而另一個 Apartment (Apartment 2) 需要使用,Apartment 1 必須呼叫 CoMarshalInterThreadInterfaceInStream 來封送處理介面。 此函式所建立的數據流是安全線程的,而且必須儲存在 Apartment 2 可存取的變數中。 Apartment 2 必須將此串流傳遞至 CoGetInterfaceAndReleaseStream 以取消介面,並取得可存取介面的 Proxy 指標。 主要 Apartment 必須保持運作,直到用戶端完成所有 COM 工作為止(因為某些同進程物件會載入主要 Apartment 中,如同進程伺服器線程問題中所述)。 以這種方式在線程之間傳遞一個對象之後,將介面指標當做參數傳遞很容易。 如此一來,分散式 COM 會針對應用程式執行封送處理和線程切換。

若要處理相同進程內其他進程和 Apartment 的呼叫,每個單個線程 Apartment 都必須有訊息迴圈。 這表示線程的工作函式必須有 GetMessage/DispatchMessage 迴圈。 如果使用其他同步處理基本類型在線程之間通訊, 則 MsgWaitForMultipleObjects 函式可用來等候訊息和線程同步處理事件。 此函式的檔有這種組合迴圈的範例。

COM 會使用每個單個線程 Apartment 中的 Windows 類別 “OleMainThreadWndClass” 來建立隱藏視窗。 呼叫物件時,會收到這個隱藏視窗的視窗訊息。 當物件的 Apartment 擷取並分派訊息時,隱藏的視窗將會收到它。 視窗程序接著會呼叫 對象的對應介面方法。

當多個用戶端呼叫物件時,呼叫會在消息佇列中排入佇列,而且物件會在每次其 Apartment 擷取和分派訊息時收到呼叫。 由於呼叫會由 COM 同步處理,而且呼叫一律由屬於物件 Apartment 的線程傳遞,因此對象的介面實作不需要提供同步處理。 單個線程 Apartment 可以實 作 IMessageFilter ,以允許它們在必要時取消呼叫或接收視窗訊息。

如果其中一個介面方法實作擷取並分派訊息,或對另一個線程進行 ORPC 呼叫,則物件可以重新進入,因而導致另一個呼叫傳遞至物件(由相同的 Apartment)。 OLE 不會防止相同線程重新進入,但有助於提供線程安全性。 這與視窗程式在處理訊息時擷取和分派訊息的方式相同。 不過,呼叫另一部單個線程 Apartment 伺服器的跨進程單個線程 Apartment 伺服器,將允許重新進入第一部伺服器。

跨 Apartment 存取介面

選擇線程模型

多線程公寓

進程伺服器線程問題

進程、線程和 Apartment

單個線程和多線程通訊