Поделиться через


Описание и работа моделей потоков 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 с помощью сообщений окна, как описано далее в этой статье.

Основной объект STA — это поток, который вызывает CoInitialize или CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) сначала в рамках заданного процесса. Основной элемент STA процесса должен оставаться в живых, пока не будет завершена работа COM, так как некоторые объекты в proc всегда загружаются в основной 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 имеет один или несколько stA и не имеет MTA. Процесс модели MTA имеет один MTA с одним или несколькими потоками и не имеет маркеров сертификации. Процесс смешанной модели имеет один MTA и любое количество stAs.

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

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

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

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

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

Примечание.

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

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

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

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

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

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

Примечание.

Текущие реализации COM позволяют потоку, который явно не инициализирует COM, быть частью MTA. Поток, который не инициализирует COM, является частью MTA, только если он начинает использовать COM после хотя бы одного другого потока в процессе, который ранее вызывается CoInitializeEx(NULL, COINIT_MULTITHREADED). (Возможно, что само COM-приложение может инициализировать MTA, если ни какой клиентский поток явно не сделал этого; например, поток, связанный с вызовами CoGetClassObject/CoCreateInstance[Ex] STA в 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 и один или несколько stAs. Указатели интерфейса должны быть маршалированы между всеми квартирами, но могут использоваться без маршалинга в MTA. Вызовы объектов в STA синхронизируются COM для выполнения только в одном потоке, а вызовы объектов в MTA не выполняются. Однако вызовы из STA в MTA обычно проходят через системный код и переключаются с потока STA на поток MTA перед доставкой в объект.

Примечание.

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

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

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

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

Маркировка поддерживаемой модели потоков для серверов в 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  3. Сервер in-proc, поддерживающий только MTA, помеченный следующим образом ThreadingModel=Free:

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

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

    Объект, предоставляемый этим сервером, реализует собственную синхронизацию и обращается одновременно несколькими клиентскими квартирами. Кроме того, этот объект создается и используется напрямую, а не через прокси-сервер, в stAs или MTA клиентского процесса. Так как этот объект используется непосредственно в stAs, сервер должен маршалировать интерфейсы объектов, возможно, с других серверов, между потоками, чтобы обеспечить доступ к любому объекту в потокообразующем способе. Кроме того, помечая модель потоков объекта как "Оба", реализующий объект указывает следующее: этот объект может вызываться из любого клиентского потока, но все обратные вызовы из этого объекта клиенту будут выполняться только в квартире, в которой объект получил указатель интерфейса на объект обратного вызова. 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 взаимодействовать с объектами с одним потоком в proc путем создания и доступа к объекту в основном STA клиента и маршалинге его клиенту STA, который вызывается CoCreateInstance[Ex].

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

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

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

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

  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), но они в настоящее время не кажутся полезными.

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

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

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

  • клиент выполняется в потоке, связанном с "main" STA (первый поток для вызова CoInitialize или COINIT_APARTMENTTHREADED CoInitializeEx флага)-вызов этого STA0 (также называемой моделью однопотокового подключения).
  • клиент работает в потоке, связанном с любым другим методом STA [ASCII 150], вызывает этот STA*.
  • клиент выполняется в потоке, связанном с 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

Ссылки

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