Описание и работа моделей потоков OLE

В этой статье описываются модели потоков OLE.

Исходная версия продукта: Модели потоков OLE
Исходный номер базы знаний: 150777

Сводка

COM-объекты можно использовать в нескольких потоках процесса. Термины "Однопотоковое квартира" (STA) и "Многопоточная квартира" (MTA) используются для создания концептуальной платформы для описания связи между объектами и потоками, связей параллелизма между объектами, средств, с помощью которых вызовы методов доставляются объекту, и правил передачи указателей интерфейса между потоками. Компоненты и их клиенты выбирают между двумя моделями квартир, которые в настоящее время поддерживаются COM:

  1. Модель однопотоковых квартир (STA). Один или несколько потоков в процессе используют COM, а вызовы объектов COM синхронизируются с помощью COM. Интерфейсы маршалируются между потоками. Вырожденный случай однопоточной модели квартиры, где только один поток в данном процессе использует COM, называется однопотоковой моделью. Предыдущие иногда называли модель STA просто "моделью квартиры".

  2. Модель многопоточных квартир (MTA): один или несколько потоков используют COM, а вызовы com-объектов, связанных с MTA, выполняются непосредственно всеми потоками, связанными с MTA, без какого-либо взаимодействия системного кода между вызывающим объектом и объектом. Так как несколько одновременных клиентов могут вызывать объекты более или менее одновременно (одновременно в многопроцессорных системах), объекты должны самостоятельно синхронизировать свое внутреннее состояние. Интерфейсы не маршалируются между потоками. Предыдущие иногда называли эту модель "моделью со свободным потоком".

  3. Модель STA и модель MTA можно использовать в одном процессе. Иногда это называется процессом смешанной модели.

MTA представлен в NT 4.0 и доступен в Windows 95 с DCOM95. Модель STA существует в Windows NT 3.51 и Windows 95, а также NT 4.0 и Windows 95 с DCOM95.

Обзор

Потоковые модели в COM предоставляют механизм для совместной работы компонентов, использующих различные архитектуры потоков. Они также предоставляют службы синхронизации для компонентов, которым они требуются. Например, конкретный объект может быть разработан для вызова только одним потоком и не может синхронизировать одновременные вызовы от клиентов. Если такой объект вызывается одновременно несколькими потоками, он приводит к сбою или возникновению ошибок. COM предоставляет механизмы для решения этой проблемы взаимодействия архитектур потоков.

Даже компонентам, поддерживающим потоки, часто требуются службы синхронизации. Например, компоненты с графическим пользовательским интерфейсом (GUI), такие как элементы управления OLE/ActiveX, активные внедрения на месте и документы ActiveX, требуют синхронизации и сериализации COM-вызовов и оконных сообщений. COM предоставляет эти службы синхронизации, чтобы эти компоненты можно было писать без сложного кода синхронизации.

"Квартира" имеет несколько взаимосвязанных аспектов. Во-первых, это логическая конструкция для размышлений о параллелизме, например о том, как потоки связаны с набором COM-объектов. Во-вторых, это набор правил, которые программисты должны соблюдать, чтобы получить поведение параллелизма, ожидаемое от com-среды. Наконец, это системный код, который помогает программистам управлять параллелизмом потоков по отношению к COM-объектам.

Термин "квартира" происходит от метафоры, в которой процесс задуман как дискретная сущность, например "здание", которое подразделяется на набор связанных, но различных "языковых стандартов", называемых "квартиры". Квартира — это логический контейнер, который создает связь между объектами и, в некоторых случаях, потоками. Потоки не являются квартирами, хотя в модели STA может быть один поток, логически связанный с квартирой. Объекты не являются квартирами, хотя каждый объект связан с одной и только одной квартирой. Но квартиры — это не просто логическая конструкция; их правила описывают поведение com-системы. Если правила моделей квартир не соблюдаются, COM-объекты будут работать неправильно.

Дополнительные сведения

Однопотоковый объект (STA) — это набор COM-объектов, связанных с определенным потоком. Эти объекты связываются с квартирой путем создания потоком или, точнее, первого предоставления com-системе (обычно путем маршалинга) в потоке. AN STA считается местом, где "живет" объект или прокси-сервер. Если к объекту или прокси-серверу требуется доступ из другой квартиры (в том же или другом процессе), указатель интерфейса должен быть маршалирован в ту квартиру, где создается новый прокси-сервер. Если соблюдаются правила модели квартир, прямые вызовы из других потоков в том же процессе не допускаются для этого объекта; это нарушает правило, в результате чего все объекты в заданной квартире выполняются в одном потоке. Правило существует, так как большинство кода, выполняющегося в STA, не будет работать должным образом при выполнении в дополнительных потоках.

Поток, связанный с STA, должен вызывать CoInitialize или CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) и получать и отправлять сообщения окна для связанных объектов для приема входящих вызовов. COM отправляет и синхронизирует вызовы объектов в STA с помощью оконных сообщений, как описано далее в этой статье.

"main STA" — это поток, который вызывает CoInitialize или CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) первым в рамках данного процесса. Main STA процесса должен оставаться активным до завершения всей работы COM, так как некоторые внутрипроцессные объекты всегда загружаются в main STA, как описано далее в этой статье.

Windows NT 4.0 и DCOM95 представляют новый тип квартиры под названием многопотоковая квартира (MTA). MTA — это набор COM-объектов, связанных с набором потоков в процессе, поэтому любой поток может вызывать любую реализацию объекта напрямую без интерпозиции системного кода. Указатели интерфейса на любой объект в MTA могут передаваться между потоками, связанными с MTA, без необходимости маршалирования. Все вызывающие потоки процесса CoInitializeEx(NULL, COINIT_MULTITHREADED) связаны с MTA. В отличие от описанного выше STA, потокам в MTA не требуется получать и отправлять сообщения окна для связанных объектов для приема входящих вызовов. COM не синхронизирует вызовы объектов в MTA. Объекты в MTA должны защищать свое внутреннее состояние от повреждения взаимодействием нескольких одновременных потоков, и они не могут делать никаких предположений о том, что содержимое хранилища Thread-Local остается константой между разными вызовами методов.

Процесс может иметь любое количество STA, но не более одного MTA. MTA состоит из одного или нескольких потоков. У АПП есть по одному потоку. Нить принадлежит, по крайней мере, одной квартире. Объекты принадлежат одной и только одной квартире. Указатели интерфейса всегда должны маршалироваться между квартирами (хотя результатом маршалинга может быть прямой указатель, а не прокси-сервер). См. сведения ниже о CoCreateFreeThreadedMarshaler.

Процесс выбирает одну из моделей потоков, предоставляемых COM. Процесс модели STA имеет один или несколько соглашений об уровне обслуживания и не имеет MTA. Процесс модели MTA содержит один MTA с одним или несколькими потоками и не содержит никаких соглашений об уровне обслуживания. Процесс смешанной модели имеет один MTA и любое количество STA.

Однопоточная модель квартиры

Поток STA должен вызывать CoInitialize или CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) и должен извлекать и отправлять сообщения окна, так как COM использует оконные сообщения для синхронизации и отправки вызовов к объекту в этой модели. Дополнительные сведения см. в разделе ССЫЛКИ ниже.

Сервер, поддерживающий модель STA:

В модели STA вызовы объекта синхронизируются com так же, как и сообщения о окне, размещенные в окне. Вызовы доставляются с помощью оконных сообщений в поток, создавший объект . Следовательно, поток объекта должен вызывать Get/PeekMessage и DispatchMessage принимать вызовы. COM создает скрытое окно, связанное с каждым STA. Вызов объекта из-за пределов STA передается com-средой выполнения в поток объекта с помощью сообщения окна, размещенного в этом скрытом окне. Когда поток, связанный с STA объекта, получает и отправляет сообщение, его получает процедура окна для скрытого окна, также реализованная COM. Процедура окна используется com-средой выполнения для "перехвата" потока, связанного с STA, так как среда выполнения COM находится по обе стороны вызова из потока, принадлежащего COM, к потоку STA. Среда выполнения COM (в настоящее время выполняется в потоке STA) вызывает "вверх" через заглушку, предоставленную COM, в соответствующий метод интерфейса объекта . Путь выполнения, возвращающийся из вызова метода, отменяет вызов up; вызов возвращается в заглушку и среду выполнения COM, которая передает управление потоку среды выполнения COM через сообщение окна, которое затем возвращается через COM-канал исходному вызывающому объекту.

Когда несколько клиентов вызывают объект STA, вызовы автоматически помещаются в очередь в очереди сообщений путем передачи механизма управления, используемого в STA. Объект получает вызов каждый раз, когда его STA извлекает и отправляет сообщения. Так как вызовы синхронизируются com таким образом и вызовы всегда выполняются в одном потоке, связанном с STA объекта, реализация интерфейса объекта не требует синхронизации.

Примечание.

Объект можно повторно ввести, если реализация метода интерфейса извлекает и отправляет сообщения во время обработки вызова метода, что приводит к другому вызову объекта с помощью того же STA. Обычно это происходит, если объект STA выполняет вызов с помощью COM. Это идентично способу повторного ввода процедуры окна, если она извлекает и отправляет сообщения во время обработки сообщения. COM не предотвращает повторный вход в тот же поток, но предотвращает параллельное выполнение. Он также предоставляет средства, с помощью которых можно управлять повторным размещением com.com. Дополнительные сведения см. в разделе ССЫЛКИ ниже. Объект не вводится повторно, если реализации метода не вызывают из своей квартиры или иным образом не получают и не отправляют сообщения.

Обязанности клиента в модели STA:

Клиентский код, выполняемый в процессе и (или) потоке, использующего модель STA, должен маршалирование интерфейсов объекта между квартирами с помощью CoMarshalInterThreadInterfaceInStream и CoGetInterfaceAndReleaseStream. Например, если в квартире 1 в клиенте есть указатель интерфейса, а в квартире 2 требуется его использование, квартира 1 должна маршалалить интерфейс с помощью CoMarshalInterThreadInterfaceInStream. Объект потока, возвращаемый этой функцией, является потокобезопасным, и его указатель интерфейса должен храниться в прямой переменной памяти, доступной apartment 2. Квартира 2 должна передать этот интерфейс CoGetInterfaceAndReleaseStream потока в , чтобы отменить удаление интерфейса на базовом объекте и вернуть указатель на прокси-сервер, через который он может получить доступ к объекту.

Main квартира данного процесса должна оставаться активной до тех пор, пока клиент не завершит всю работу COM, так как некоторые объекты in-proc загружаются в main-квартире. (Дополнительные сведения см. ниже).

Модель многопотоковых квартир

MTA — это коллекция объектов, созданных или предоставляемых всеми потоками процесса, которые вызвали CoInitializeEx(NULL, COINIT_MULTITHREADED).

Примечание.

Текущие реализации COM позволяют потоку, который явно не инициализирует COM, быть частью MTA. Поток, который не инициализирует COM, является частью MTA только в том случае, если он начинает использовать COM после того, как по крайней мере один другой поток в процессе ранее вызвал CoInitializeEx(NULL, COINIT_MULTITHREADED). (Возможно, даже, что COM сам инициализировал MTA, если ни в каких клиентских потоках это не было сделано явным образом. Например, поток, связанный с STA, вызывает CoGetClassObject/CoCreateInstance[Ex] в идентификаторе CLSID, помеченном как ThreadingModel=Free, и COM неявно создает MTA, в который загружается объект класса.) См. сведения о взаимодействии потоковой модели ниже.

Однако это конфигурация, которая может вызвать проблемы, такие как нарушения доступа, при определенных обстоятельствах. Поэтому рекомендуется, чтобы каждый поток, который должен выполнять работу COM, инициализировал COM путем вызова CoInitializeEx , а затем, по завершении работы COM, вызовите CoUninitialize. Затраты на "ненужную" инициализацию MTA минимальны.

Потокам MTA не требуется получать и отправлять сообщения, так как COM не использует сообщения окна в этой модели для доставки вызовов объекта.

  • Сервер, поддерживающий модель MTA:

    В модели MTA вызовы объекта не синхронизируются COM. Несколько клиентов могут одновременно вызывать объект, который поддерживает эту модель в разных потоках, и объект должен обеспечить синхронизацию в своих реализациях интерфейса или метода с помощью объектов синхронизации, таких как события, мьютексы, семафоры и т. д. Объекты MTA могут получать одновременные вызовы от нескольких внепроцессных клиентов через пул потоков, созданных COM, принадлежащих процессу объекта. Объекты MTA могут принимать одновременные вызовы от нескольких внутрипроцессных клиентов в нескольких потоках, связанных с MTA.

  • Обязанности клиента в модели MTA:

    Клиентский код, выполняемый в процессе и (или) потоке, использующий модель MTA, не должен маршалировать указатели интерфейса объекта между собой и другими потоками MTA. Вместо этого один поток MTA может использовать указатель интерфейса, полученный из другого потока MTA, в качестве прямого указателя на память. Когда клиентский поток выполняет вызов внепроцессного объекта, он приостанавливается до завершения вызова. Вызовы могут поступать на объекты, связанные с MTA, в то время как все потоки, созданные приложением, связанные с MTA, блокируются при выполнении вызовов. В этом случае и в целом входящие вызовы доставляются в потоках, предоставляемых com-средой выполнения. Фильтры сообщений (IMessageFilter) недоступны для использования в модели MTA.

Модели со смешанными потоками

Процесс, поддерживающий модель смешанного потока, будет использовать один MTA и один или несколько АПП. Указатели интерфейса должны маршалироваться между всеми квартирами, но их можно использовать без маршалинга в MTA. Вызовы объектов в STA синхронизируются COM для выполнения только в одном потоке, а вызовы объектов в MTA — нет. Однако вызовы из STA в MTA обычно проходят через предоставленный системой код и переключаются с потока STA на поток MTA перед доставкой в объект.

Примечание.

Сведения о случаях, в которых можно использовать прямые указатели, и о том, как поток STA может вызывать непосредственно в объект, связанный с MTA, и наоборот, из нескольких квартир см. в документации по CoCreateFreeThreadedMarshaler() пакету SDK и описанию этого API ниже.

Выбор потоковой модели

Компонент может выбрать поддержку модели STA, модели MTA или их сочетания с помощью смешанной потоковой модели. Например, объект, который выполняет обширные операции ввода-вывода, может выбрать поддержку MTA, чтобы обеспечить максимальный ответ клиентам, разрешая вызовы интерфейса во время задержки ввода-вывода. Кроме того, объект, взаимодействующий с пользователем, почти всегда выбирает поддержку STA для синхронизации входящих COM-вызовов с его операциями графического пользовательского интерфейса. Поддержка модели STA упрощается, так как COM обеспечивает синхронизацию. Поддержка модели MTA сложнее, так как объект должен реализовывать синхронизацию, но лучше отвечать на запросы клиентов, так как синхронизация используется для небольших разделов кода, а не для всего вызова интерфейса, предоставляемого COM.

Модель STA также используется Microsoft Transaction Server (MTS, ранее — Viper), поэтому объекты на основе DLL, планирующие выполняться в среде MTS, должны использовать модель STA. Объекты, реализованные для модели MTA, обычно хорошо работают в среде MTS. Однако они будут работать менее эффективно, так как будут использовать ненужные примитивы синхронизации потоков.

Маркировка поддерживаемой модели потоков для серверов In-Proc

Поток использует модель MTA, если он вызывает CoInitializeEx(NULL, COINIT_MULTITHREADED) или использует COM без инициализации. Поток использует модель STA, если вызывает CoInitialize или CoInitializeEx(NULL, COINIT_APARTMENTTHREADED).

CoInitialize API-интерфейсы обеспечивают управление блоками для клиентского кода и объектов, которые упаковываются в . EXEs, так как код запуска среды выполнения COM может инициализировать COM нужным образом.

Однако com-сервер на основе DLL не вызывается CoInitialize/CoInitializeEx , так как эти API будут вызваны к моменту загрузки сервера DLL. Таким образом, сервер DLL должен использовать реестр для информирования COM о модели потоков, которую он поддерживает, чтобы COM удостоверял, что система работает совместимым с ней способом. Для этой цели используется именованное значение вызываемого ThreadingModel ключа CLSID\InprocServer32 компонента следующим образом:

  • ThreadingModel значение не указано: поддерживает однопотоковую модель.
  • ThreadingModel=Apartment: поддерживает модель STA.
  • ThreadingModel=Both: поддерживает модель STA и MTA.
  • ThreadingModel=Free: поддерживает только MTA.

Примечание.

ThreadingModel является именованным значением, а не подразделом InprocServer32, как неправильно описано в некоторых более ранних версиях документации По Win32.

Потоковые модели серверов in-proc рассматриваются далее в этой статье. Если сервер in-proc предоставляет множество типов объектов (каждый с собственным уникальным идентификатором CLSID), каждый тип может иметь разные ThreadingModel значения. Другими словами, потоковая модель выполняется по идентификатору CLSID, а не по каждому пакету кода или БИБЛИОТЕКе DLL. Однако точки входа API, необходимые для начальной загрузки и запроса всех серверов in-proc (DLLGetClassObject(), DLLCanUnloadNow()), должны быть потокобезопасными для любого сервера, поддерживающего несколько потоков (то есть ThreadingModel значение Apartment, Оба или Бесплатный).

Как уже говорилось ранее, внепроцессные серверы не помечаются с помощью значения ThreadingModel. Вместо этого они используют CoInitialize или CoInitializeEx. Серверы на основе DLL, которые должны работать вне процесса с использованием функции "суррогатной" COM (например, суррогатной DLLHOST.EXE, предоставляемой системой), следуют правилам для серверов на основе DLL; в этом случае особых соображений нет.

Когда клиент и объект используют разные потоковые модели

Взаимодействие между клиентом и внепроцессным объектом является прямым, даже если используются различные потоковые модели, так как клиент и объект находятся в разных процессах, а COM участвует в передаче вызовов от клиента к объекту. Так как COM пересекается между клиентом и сервером, он предоставляет код для взаимодействия потоковых моделей. Например, если объект STA вызывается одновременно несколькими клиентами STA или MTA, COM синхронизирует вызовы, помещая соответствующие сообщения окна в очередь сообщений сервера. STA объекта получает один вызов каждый раз, когда извлекает и отправляет сообщения. Все сочетания взаимодействия потоковой модели разрешены и полностью поддерживаются между клиентами и внепроцессными объектами.

Взаимодействие между клиентом и объектом in-proc, использующим различные потоковые модели, является более сложным. Несмотря на то, что сервер работает в режиме proc, в некоторых случаях COM должен взаимодействовать между клиентом и объектом . Например, объект in-proc, предназначенный для поддержки модели STA, может вызываться одновременно несколькими потоками клиента. COM не может разрешить клиентским потокам прямой доступ к интерфейсу объекта, так как объект не предназначен для такого параллельного доступа. Вместо этого COM должен убедиться, что вызовы синхронизируются и выполняются только потоком, связанным с STA, который "содержит" объект . Несмотря на дополнительную сложность, все сочетания взаимодействия потоковой модели разрешены между клиентами и объектами in-proc.

Потоковые модели на непроцессных серверах (на основе EXE)

Ниже приведены три категории внепроцессных серверов, каждый из которых может использоваться любым COM-клиентом независимо от модели потоков, используемой этим клиентом.

  1. Сервер модели STA:

    Сервер выполняет работу COM в одном или нескольких приложениях STA. Входящие вызовы синхронизируются com и доставляются потоком, связанным с STA, в котором был создан объект . Вызовы методов фабрики классов доставляются потоком, связанным с STA, который зарегистрировал фабрику классов. Объекту и фабрике классов не требуется реализовывать синхронизацию. Однако реализующий должен синхронизировать доступ к любым глобальным переменным, используемым несколькими службами STA. Сервер должен использовать CoMarshalInterThreadInterfaceInStream и CoGetInterfaceAndReleaseStream для маршалирования указателей интерфейса, возможно, с других серверов, между stA. При необходимости сервер может реализовать IMessageFilter в каждом STA для управления аспектами доставки вызовов с помощью COM. Вырожденным вариантом является однопоточный сервер модели, который выполняет com-работу в одном STA.

  2. Сервер модели MTA:

    Сервер выполняет работу COM в одном или нескольких потоках, все из которых принадлежат MTA. Вызовы не синхронизируются с помощью COM. COM создает пул потоков в серверном процессе, и вызов клиента доставляется любым из этих потоков. Потокам не требуется получать и отправлять сообщения. Фабрика объектов и классов должна реализовать синхронизацию. Серверу не нужно маршалировать указатели интерфейса между потоками.

  3. Сервер смешанной модели:

    Дополнительные сведения см. в разделе этой статьи под названием "Смешанная потоковая модель".

Потоковые модели в клиентах

Существует три категории клиентов:

  1. Клиент модели STA:

    Клиент выполняет com-работу в потоках, связанных с одним или несколькими соглашениями об уровне обслуживания. Клиент должен использовать CoMarshalInterThreadInterfaceInStream и CoGetInterfaceAndReleaseStream для маршалирования указателей интерфейса между stA. Вырожденным вариантом является клиент однопоточной модели, который выполняет com-работу в одном STA. Поток клиента входит в цикл сообщений, предоставленный COM, при выполнении исходящего вызова. Клиент может использовать для IMessageFilter управления обратными вызовами и обработки оконных сообщений во время ожидания исходящих вызовов и других проблем с параллелизмом.

  2. Клиент модели MTA:

    Клиент выполняет работу COM в одном или нескольких потоках, все из которых принадлежат MTA. Клиенту не нужно маршалировать указатели интерфейса между своими потоками. Клиент не может использовать IMessageFilter. Потоки клиента приостанавливаются при выполнении COM-вызова внепроцессного объекта и возобновляются при возврате вызова. Входящие вызовы поступают в созданные и управляемые COM потоки.

  3. Клиент смешанной модели:

    Дополнительные сведения см. в разделе этой статьи под названием "Смешанная потоковая модель".

Потоковые модели на серверах на основе DLL

Существует четыре категории серверов in-proc, каждый из которых может использоваться любым COM-клиентом независимо от модели потоков, используемой этим клиентом. Тем не менее, серверы in-proc должны предоставлять код маршалинга для любого пользовательского (не определяемого системой) интерфейса, который они реализуют, если они поддерживают взаимодействие с потоковой моделью, так как обычно требуется маршалировать интерфейс COM между клиентскими квартирами. Ниже приведены четыре категории:

  1. Сервер in-proc, поддерживающий однопотоковую ("main" STA), не имеет ThreadingModel значения:

    Объект, предоставленный этим сервером, должен получить доступ к тому же клиенту STA, с помощью которого он был создан. Кроме того, сервер ожидает, что все его точки входа, такие как DllGetClassObject и DllCanUnloadNow, и глобальные данные будут доступны одному потоку (связанному с main STA). Серверы, которые существовали до появления многопотоковых в COM, относятся к этой категории. Эти серверы не предназначены для доступа нескольких потоков, поэтому COM создает все объекты, предоставляемые сервером в main STA процесса, а вызовы объектов доставляются потоком, связанным с main STA. Другие клиентские квартиры получают доступ к объекту через прокси-серверы. Вызовы из других квартир переходят от прокси-сервера к заглушку в main STA (маршалинг между потоками), а затем к объекту . Это маршалирование позволяет COM синхронизировать вызовы к объекту, а вызовы доставляются sta, в котором был создан объект. Маршалинг между потоками выполняется медленно по сравнению с прямым вызовом, поэтому рекомендуется переписать эти серверы для поддержки нескольких stA (категория 2).

  2. In-proc Server, поддерживающий однопотоковую модель квартиры (несколько STA), помеченный следующим образом ThreadingModel=Apartment:

    Объект, предоставленный этим сервером, должен получить доступ к тому же клиенту STA, с помощью которого он был создан. Таким образом, он похож на объект, предоставляемый однопоточным сервером in-proc. Однако объекты, предоставляемые этим сервером, могут создаваться в нескольких центрах обслуживания процесса, поэтому сервер должен проектировать свои точки входа, такие как DllGetClassObject и DllCanUnloadNow, и глобальные данные для многопотокового использования. Например, если два приложения stA в процессе создают два экземпляра объекта in-proc одновременно, DllGetClassObject может вызываться одновременно обоими центрами АПП. Аналогичным образом необходимо написать, DllCanUnloadNow чтобы сервер был защищен от выгрузки во время выполнения кода на сервере.

    Если сервер предоставляет только один экземпляр фабрики классов для создания всех объектов, реализация фабрики классов также должна быть разработана для многопотокового использования, так как к ней обращаются несколько клиентских stas. Если сервер создает новый экземпляр фабрики классов при каждом DllGetClassObject вызове, фабрика классов не должна быть потокобезопасной. Однако исполнитель должен синхронизировать доступ к любым глобальным переменным.

    COM-объекты, созданные фабрикой классов, не должны быть потокобезопасны. Однако доступ к глобальным переменным должен быть синхронизирован исполнителем. После создания потоком доступ к объекту всегда осуществляется через этот поток, и все вызовы объекта синхронизируются с помощью COM. Клиентские квартиры, которые отличаются от STA, в котором был создан объект, должны обращаться к объекту через прокси-серверы. Эти прокси-серверы создаются, когда клиент маршалирует интерфейс между своими квартирами.

    Любой клиент, создающий объект STA через фабрику классов, получает прямой указатель на объект . Это отличается от однопоточных объектов in-proc, где только main STA клиента получает прямой указатель на объект, а все остальные объекты STA, создающие объект, получают доступ к объекту через прокси-сервер. Так как маршалинг между потоками выполняется медленно по сравнению с прямым вызовом, скорость можно повысить, изменив однопоточный сервер in-proc для поддержки нескольких stA.

  3. In-proc Server, поддерживающий только MTA, помеченный как ThreadingModel=Free:

    Объект, предоставляемый этим сервером, безопасен только для MTA. Он реализует собственную синхронизацию и получает доступ к нескольким клиентским потокам одновременно. Этот сервер может иметь поведение, несовместимое с моделью STA. (Например, путем использования очереди сообщений Windows таким образом, чтобы прерывать работу насоса сообщений STA.) Кроме того, помечая потоковую модель объекта как "Свободную", исполнитель объекта заявляет следующее: этот объект можно вызвать из любого клиентского потока, но этот объект также может передавать указатели интерфейса напрямую (без маршалинга) в созданные потоки, и эти потоки могут выполнять вызовы через эти указатели. Таким образом, если клиент передает этому объекту указатель интерфейса на реализованный клиентом объект (например, приемник), он может выбрать обратный вызов через этот указатель интерфейса из любого созданного потока. Если клиент является STA, прямой вызов из потока, который отличается от потока, создавшего объект приемника, будет иметь ошибку (как показано в 2 выше). Поэтому COM всегда гарантирует, что клиенты в потоках, связанных с STA, получают доступ к этому типу объекта in-proc только через прокси-сервер. Кроме того, эти объекты не должны агрегироваться с маршалером свободных потоков, так как это позволяет выполнять их непосредственно в потоках STA.

  4. In-proc Server, поддерживающий модель квартиры и свободное потоковое управление, помеченный как ThreadingModel=Both:

    Объект, предоставляемый этим сервером, реализует собственную синхронизацию и одновременно обращается к нескольким клиентским квартирам. Кроме того, этот объект создается и используется непосредственно, а не через прокси-сервер, в службах stas или MTA клиентского процесса. Так как этот объект используется непосредственно в stA, сервер должен маршалировать интерфейсы объектов, возможно, с других серверов, между потоками, чтобы гарантировать доступ к любому объекту соответствующим потоком способом. Кроме того, помечая потоковую модель объекта как "Оба", реализующий объект указывает следующее: этот объект можно вызвать из любого клиентского потока, но любые обратные вызовы из этого объекта в клиент будут выполняться только в той квартире, в которой объект получил указатель интерфейса на объект обратного вызова. COM позволяет создать такой объект непосредственно в STA, а также в MTA клиентского процесса.

    Так как любая квартира, которая создает такой объект, всегда получает прямой указатель, а не прокси-указатель, ThreadingModel "Both" объекты обеспечивают повышение производительности по сравнению с ThreadingModel "Free" объектами при загрузке в STA.

    ThreadingModel "Both" Так как объект также предназначен для доступа MTA (он является потокобезопасной внутренне), он может ускорить производительность путем агрегирования с маршалером, предоставляемым CoCreateFreeThreadedMarshaler. Этот системный объект агрегируется с любыми вызывающими объектами, а пользовательские маршалы направляют указатели на объект во все помещения в процессе. Клиенты в любой квартире, будь то STA или MTA, могут получить доступ к объекту напрямую, а не через прокси-сервер. Например, клиент модели STA создает объект in-proc в STA1 и маршалирует объект в STA2. Если объект не объединяется с маршалером со свободным потоком, STA2 получает доступ к объекту через прокси-сервер. Если это так, маршалер со свободным потоком предоставляет STA2 с прямым указателем на объект

    Примечание.

    Необходимо соблюдать осторожность при агрегации с помощью маршалера со свободным потоком. В качестве примера предположим, что объект, помеченный как ThreadingModel "Both" (а также агрегируемый с помощью маршалера со свободным потоком), имеет элемент данных, который является указателем интерфейса на другой объект ThreadingModel с именем "Apartment". Затем предположим, что STA создает первый объект, а во время создания первый объект создает второй объект. Согласно правилам, описанным выше, первый объект теперь содержит прямой указатель на второй объект. Теперь предположим, что STA маршалирует указатель интерфейса на первый объект в другую квартиру. Так как первый объект объединяется с маршалером со свободным потоком, прямой указатель на первый объект предоставляется во вторую квартиру. Если вторая квартира вызывает через этот указатель и если этот вызов вызывает первый объект через указатель интерфейса на второй объект, то произошла ошибка, так как второй объект не должен вызываться напрямую из второй квартиры. Если первый объект содержит указатель на прокси-сервер второго объекта, а не прямой указатель, это приведет к другой ошибке. Системные прокси-серверы также являются COM-объектами, которые связаны с одной и только одной квартирой. Они следят за своей квартирой, чтобы избежать определенных циклов. Таким образом, объект, вызывающий на прокси-сервере, связанном с квартирой, отличной от потока, в котором выполняется объект, получит RPC_E_WRONG_THREAD возврат от прокси-сервера, и вызов завершится ошибкой.

Взаимодействие потоковой модели между клиентами и внутрипроцессными объектами

Между клиентами и внутрипроцессными объектами разрешены все сочетания взаимодействия потоковой модели.

COM позволяет всем клиентам модели STA взаимодействовать с однопоточными объектами in-proc, создавая объект в main STA клиента и маршалируя его для клиента STA, который называется CoCreateInstance[Ex].

Если MTA в клиенте создает модель STA в proc-сервере, COM запускает в клиенте sta-адрес узла. Этот sta узла создает объект, и указатель интерфейса маршалируется обратно в MTA. Аналогичным образом, когда STA создает сервер MTA in-proc, COM запускает узел MTA, в котором создается объект и маршалируется обратно в STA. Взаимодействие между однопоточной моделью и моделью MTA обрабатывается аналогичным образом, так как однопоточная модель является просто дегенеративным вариантом модели STA.

Код маршалинга должен быть предоставлен для любого пользовательского интерфейса, реализуемого сервером in-proc, если он хочет поддерживать взаимодействие, требующее com для маршалинга интерфейса между клиентскими квартирами. Дополнительные сведения см. в разделе ССЫЛКИ ниже.

Связь между потоковой моделью и возвращенным объектом фабрики класса

Точное определение встроенных серверов, которые "загружаются в" квартиры, объясняется на двух следующих шагах:

  1. Если загрузчик операционной системы не загрузил библиотеку DLL, содержащую класс сервера in-proc ( сопоставленный с адресным пространством процесса), эта операция выполняется, и COM получает адрес DLLGetClassObject функции, экспортируемой библиотекой DLL. Если библиотека DLL ранее была загружена потоком, связанным с каким-либо объектом, этот этап пропускается.

  2. COM использует поток (или, в случае MTA, один из потоков), связанный с "загрузочной" квартирой, для вызова DllGetClassObject функции, экспортируемой библиотекой DLL, запрашивая CLSID необходимого класса. Затем возвращаемый объект фабрики используется для создания экземпляров объектов класса .

    Второй шаг (вызов DllGetClassObject com) происходит каждый раз, когда клиент вызывает CoGetClassObject/CoCreateIntance[Ex], даже из одной и той же квартиры. Другими словами, поток, связанный с одной и той же квартирой, DllGetClassObject может вызываться много раз. Все зависит от того, сколько клиентов в этой квартире пытаются получить доступ к объекту фабрики класса для этого класса.

Авторы реализаций классов и, в конечном счете, автор пакета DLL заданного набора классов имеют полную свободу решать, какой объект фабрики следует вернуть в ответ на DllGetClassObject вызов функции. Автор пакета DLL имеет право голоса, так как код "за" одной точкой входа на уровне DllGetClassObject() DLL имеет первое и потенциально окончательное право решать, что делать. Ниже приведены три типичных варианта.

  1. DllGetClassObject возвращает уникальный объект фабрики класса на вызывающий поток (то есть один объект фабрики класса на STA и потенциально несколько фабрик классов в MTA).

  2. DllGetClassObject всегда возвращает один и тот же объект фабрики класса, независимо от идентификатора вызывающего потока или типа объекта, связанного с вызывающим потоком.

  3. DllGetClassObject возвращает уникальный объект фабрики класса для каждой вызывающей квартиры (по одному на квартиру в STA и MTA).

Существуют и другие возможности для связи между вызовами и DllGetClassObject возвращаемым объектом фабрики класса (например, новый объект фабрики класса для каждого вызова DllGetClassObject) но в настоящее время они не представляются полезными.

Сводка моделей клиентских и объектных потоков для серверов In-Proc

В следующей таблице приведены сведения о взаимодействии между различными моделями потоков при первом вызове CoGetClassObject клиентского потока в классе, реализованном в качестве сервера in-proc.

Типы клиентов и потоков:

  • клиент выполняется в потоке, связанном с STA "main" (первый поток для вызова CoInitialize или CoInitializeEx с COINIT_APARTMENTTHREADED флагом). Вызовите эту модель STA0 (также называемую однопоточной моделью).
  • клиент выполняется в потоке, связанном с в любом другом вызове STA [ASCII 150].
  • клиент выполняется в потоке, связанном с в MTA.

Типы серверов DLL:

  • У сервера нет ThreadingModel ключа — вызовите это значение "Нет".
  • Сервер помечен как "Квартира" - вызовите это "Apt".
  • Сервер помечен как "Бесплатный".
  • Сервер помечен как "Оба".

При чтении приведенной ниже таблицы имейте в виду приведенное выше определение "загрузка" сервера в квартиру.

Client         Server                 Result
STA0           None                   Direct access; server loaded into STA0  
STA*           None                   Proxy access; server loaded into STA0.  
MTA            None                   Proxy access; server loaded into STA0; STA0 created automatically by COM if necessary;  
STA0           Apt                    Direct access; server loaded into STA0  
STA*           Apt                    Direct access; server loaded into STA*  
MTA            Apt                    Proxy access; server loaded into an STA created automatically by COM.
STA0           Free                   Proxy access; server is loaded into MTA MTA created automatically by COM if necessary.
STA*           Free                   Same as STA0->Free
MTA            Free                   Direct access
STA0           Both                   Direct access; server loaded into STA0
STA*           Both                   Direct access; server loaded into STA*
MTA            Both                   Direct access; server loaded into the MTA

Ссылки

Документация по пакету SDK по интерфейсу CoRegisterMessageFilter() и IMessageFilter .