Single-Threaded 公寓
使用單個線程 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 進入點。 有適用於進程內線程伺服器的特殊考慮。 如需詳細資訊,請參閱 In-Process 伺服器線程問題。
雖然多個物件可以存在於單一線程上,但任何 Apartment 模型物件都不能存在於多個線程上。
客戶端進程或跨進程伺服器的每個線程都必須呼叫 CoInitialize,或呼叫 CoInitializeEx,併為 dwCoInit 參數指定COINIT_APARTMENTTHREADED。 主要 Apartment 是先呼叫 CoInitializeEx 線程。 如需行程伺服器的詳細資訊,請參閱 In-Process 伺服器線程問題。
物件的所有呼叫都必須在其線程上進行(在其 Apartment 內)。 禁止直接從另一個線程呼叫 物件;以這個自由線程方式使用 物件可能會導致應用程式發生問題。 此規則的含意在於,在 Apartment 之間傳遞時,物件的所有指標都必須封送處理。 COM 針對此目的提供下列兩個函式:
- CoMarshalInterThreadInterfaceInStream 封送處理介面至傳回給呼叫端的數據流物件。
- CoGetInterfaceAndReleaseStream 從數據流物件取消介面指標並釋放它。
這些函式會包裝對 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 中,如 In-Process 伺服器線程問題中所述)。 以這種方式在線程之間傳遞一個對象之後,將介面指標當做參數傳遞很容易。 如此一來,分散式 COM 會針對應用程式執行封送處理和線程切換。
若要處理相同進程內其他進程和 Apartment 的呼叫,每個單個線程 Apartment 都必須有訊息迴圈。 這表示線程的工作函式必須有 GetMessage/DispatchMessage 迴圈。 如果使用其他同步處理基本類型在線程之間通訊,則 MsgWaitForMultipleObjects 函式可用來等候訊息和線程同步處理事件。 此函式的檔有這種組合迴圈的範例。
COM 會使用每個單個線程 Apartment 中的 Windows 類別 “OleMainThreadWndClass” 來建立隱藏視窗。 呼叫物件時,會收到這個隱藏視窗的視窗訊息。 當物件的 Apartment 擷取並分派訊息時,隱藏的視窗將會收到它。 視窗程序接著會呼叫 對象的對應介面方法。
當多個用戶端呼叫物件時,呼叫會在消息佇列中排入佇列,而且物件會在每次其 Apartment 擷取和分派訊息時收到呼叫。 由於呼叫會由 COM 同步處理,而且呼叫一律由屬於物件 Apartment 的線程傳遞,因此對象的介面實作不需要提供同步處理。 單個線程 Apartment 可以實作 IMessageFilter,以允許他們在必要時取消呼叫或接收視窗訊息。
如果其中一個介面方法實作擷取並分派訊息,或對另一個線程進行 ORPC 呼叫,則物件可以重新進入,因而導致另一個呼叫傳遞至物件(由相同的 Apartment)。 OLE 不會防止相同線程重新進入,但有助於提供線程安全性。 這與視窗程式在處理訊息時擷取和分派訊息的方式相同。 不過,呼叫另一部單個線程 Apartment 伺服器的跨進程單個線程 Apartment 伺服器,將允許重新進入第一部伺服器。
相關主題