Квартиры с одним потоком
Использование однопоточных квартир (процесс модели квартир) предлагает парадигму на основе сообщений для работы с несколькими объектами, работающими одновременно. Он позволяет создавать более эффективный код, разрешая потоку, пока он ожидает завершения некоторой операции, которая занимает много времени, чтобы разрешить выполнение другого потока.
Каждый поток в процессе, который инициализирован как процесс модели квартиры, и который извлекает и отправляет сообщения окна, является потоком с одним потоком квартиры. Каждый поток живет в своей квартире. В квартире указатели интерфейса могут передаваться без маршалинга, поэтому все объекты в одном потоке квартиры напрямую взаимодействуют.
Логическое группирование связанных объектов, которые выполняются в одном потоке и поэтому должны иметь синхронное выполнение, может жить в одном потоке квартиры. Однако объект модели квартиры не может находиться в нескольких потоках. Вызовы объектов в других потоках должны выполняться в контексте собственного потока, поэтому распределенные com-коммутаторы автоматически передают потоки при вызове прокси-сервера.
Межпроцессные и межпоточные модели похожи. Если необходимо передать указатель интерфейса на объект в другой квартире (в другом потоке) в рамках одного процесса, используется та же модель маршалинга, которую объекты в разных процессах используют для передачи указателей через границы процесса. Получив указатель на стандартный объект маршалинга, вы можете маршалировать указатели интерфейса через границы потоков (между квартирами) так же, как и между процессами. (Указатели интерфейса должны маршалироваться при передаче между квартирами.)
Правила для однопоточных квартир просты, но важно тщательно следовать им:
- Каждый объект должен жить только на одном потоке (внутри однопоточной квартиры).
- Инициализация COM-библиотеки для каждого потока.
- Маршал всех указателей на объекты при передаче их между квартирами.
- Каждая однопоточная квартира должна иметь цикл сообщений для обработки вызовов из других процессов и квартир в рамках одного процесса. Однопоточные квартиры без объектов (только клиента) также требуют цикла сообщений для отправки широковещательных сообщений, используемых некоторыми приложениями.
- Объекты, основанные на библиотеке DLL или в процессе, не вызывают функции инициализации COM; Вместо этого они регистрируют свою модель потоков в именованном значении ThreadingModel в разделе InprocServer32 в реестре. Объекты с поддержкой квартир также должны тщательно записывать точки входа DLL. Существуют особые рекомендации, которые применяются к потокам внутрипроцессных серверов. Дополнительные сведения см. в разделе "Проблемы потоков в процессе сервера".
Хотя несколько объектов могут жить в одном потоке, объект модели квартиры не может жить в нескольких потоках.
Каждый поток клиентского процесса или внепроцессного сервера должен вызывать CoInitialize или вызывать CoInitializeEx и указывать COINIT_APARTMENTTHREADED для параметра dwCoInit. Основная квартира — это поток, который сначала вызывает CoInitializeEx . Сведения о внутрипроцессных серверах см. в разделе "Проблемы потоков в процессе".
Все вызовы объекта должны выполняться в потоке (в пределах своей квартиры). Запрещено вызывать объект непосредственно из другого потока; использование объектов в этом свободном потоке может вызвать проблемы для приложений. Это правило связано с тем, что все указатели на объекты должны маршалироваться при передаче между квартирами. COM предоставляет следующие две функции для этой цели:
- CoMarshalInterThreadInterfaceInStream маршалирует интерфейс в объект потока, возвращаемый вызывающей объекту.
- CoGetInterfaceAndReleaseStream отменяет указатель интерфейса из объекта потока и освобождает его.
Эти функции обтекают вызовы функций CoMarshalInterface и CoUnmarshalInterface , которые требуют использования флага MSHCTX_INPROC.
Как правило, маршалинг выполняется автоматически com. Например, при передаче указателя интерфейса в качестве параметра в вызове метода на прокси-сервер в объект в другой квартире или при вызове CoCreateInstance COM выполняет маршалинг автоматически. Однако в некоторых особых случаях, когда модуль записи приложений передает указатели интерфейса между квартирами без использования обычных механизмов COM, модуль записи должен обрабатывать маршалинг вручную.
Если одна квартира (квартира 1) в процессе имеет указатель интерфейса, а другая квартира (Квартира 2) требует его использования, квартира 1 должна вызвать CoMarshalInterfaceInStream для маршалирования интерфейса. Поток, созданный этой функцией, является потокобезопасным и должен храниться в переменной, доступной в квартире 2. Квартира 2 должна передать этот поток в CoGetInterfaceAndReleaseStream , чтобы отменить маршал интерфейс и вернуть указатель на прокси-сервер, через который он может получить доступ к интерфейсу. Основная квартира должна оставаться в живых, пока клиент не завершит всю работу COM (так как некоторые внутрипроцессные объекты загружаются в главной квартире, как описано в разделе "Проблемы потоков в процессе сервера"). После передачи одного объекта между потоками таким образом очень легко передавать указатели интерфейса в качестве параметров. Таким образом распределенный COM выполняет маршалинг и переключение потоков для приложения.
Для обработки вызовов из других процессов и квартир в рамках одного процесса каждая однопоточная квартира должна иметь цикл сообщений. Это означает, что рабочая функция потока должна иметь цикл GetMessage/DispatchMessage. Если для обмена данными между потоками используются другие примитивы синхронизации, функция MsgWaitForMultipleObjects может использоваться для ожидания сообщений и событий синхронизации потоков. В документации по этой функции приведен пример этого цикла сочетания.
COM создает скрытое окно с помощью класса Windows OleMainThreadWndClass в каждой однопоточной квартире. Вызов объекта получается в виде сообщения окна в это скрытое окно. Когда квартира объекта извлекает и отправляет сообщение, скрытое окно получит его. Затем процедура окна вызовет соответствующий метод интерфейса объекта.
Когда несколько клиентов вызывают объект, вызовы помещаются в очередь сообщений, и объект будет получать вызов каждый раз, когда его квартира извлекает и отправляет сообщения. Так как вызовы синхронизируются com и вызовы всегда передаются потоком, принадлежащим к квартире объекта, реализации интерфейса объекта не должны обеспечивать синхронизацию. Однопоточные квартиры могут реализовать IMessageFilter , чтобы разрешить им отменять вызовы или получать сообщения окна при необходимости.
Объект можно повторно ввести, если одна из реализаций метода интерфейса извлекает и отправляет сообщения или выполняет вызов ORPC к другому потоку, что приводит к тому, что другой вызов будет доставлен в объект (по той же квартире). OLE не препятствует повторному входу в одном потоке, но может помочь обеспечить безопасность потока. Это идентично тому, как можно повторно ввести процедуру окна, если она извлекает и отправляет сообщения при обработке сообщения. Однако вызов внепроцессного однопоточного сервера квартиры, который вызывает другой однопоточный сервер квартиры, позволит повторно ввести первый сервер.
См. также