In-Process Проблемы с многопоточностью сервера
Сервер в процессе не вызывает CoInitialize, CoInitializeExили OleInitialize, чтобы пометить свою модель потоков. Для объектов DLL, учитывающих потоки, или объектов в процессе необходимо задать модель потоков в реестре. Модель по умолчанию, если не указать модель потоковой обработки, является однопотоковой для каждого процесса. Чтобы указать модель, добавьте значение ThreadingModel в раздел InprocServer32 в реестре.
Библиотеки DLL, поддерживающие создание экземпляра объекта класса, должны реализовывать и экспортировать функции DllGetClassObject и DllCanUnloadNow. Когда клиент запрашивает экземпляр класса, поддерживаемого библиотекой DLL, вызов CoGetClassObject (напрямую или через вызов CoCreateInstance) приводит к вызову DllGetClassObject для получения указателя на объект класса, если объект реализован в библиотеке DLL. DllGetClassObject поэтому должно иметь возможность выдавать несколько объектов класса или один потокобезопасный объект (по сути, только с помощью InterlockedIncrement/InterlockedDecrement в их внутренних счетчиках ссылок).
Как следует из названия, DllCanUnloadNow вызывается для определения того, используется ли реализующая её DLL, что позволяет вызывающему объекту безопасно выгрузить её, если это не так. Вызовы CoFreeUnusedLibraries из любого потока всегда направляются через основной поток апартамента для вызова DllCanUnloadNow.
Как и другие серверы, внутрипроцессные серверы могут быть однопоточными, многопоточными или свободными. Эти серверы могут использоваться любым клиентом OLE независимо от модели потоков, используемой этим клиентом.
Все сочетания взаимодействия с потоковой моделью разрешены между клиентами и объектами внутрипроцессного процесса. Взаимодействие между клиентом и внутрипроцессным объектом, использующего различные модели потоков, точно так же, как взаимодействие между клиентами и внепроцессными серверами. Для внутрипроцессного сервера, когда модель потоков клиента и внутрипроцессного сервера отличается, COM должен взаимодействовать между клиентом и объектом.
Если объект внутри процесса, поддерживающий однопоточную модель, вызывается одновременно несколькими потоками клиента, COM не может разрешить клиентским потокам напрямую обращаться к интерфейсу объекта", объект не был разработан для такого доступа. Вместо этого COM должен убедиться, что вызовы синхронизированы и выполняются только клиентским потоком, создающим объект. Таким образом, COM создает объект в главной квартире клиента и требует, чтобы все остальные клиентские квартиры были доступны для доступа к объекту с помощью прокси-серверов.
Когда в клиенте создается сервер внутрипроцессной модели с потоками, использующей многопоточную модель (свободный поток), COM создает поток однопоточной модели апартамента "host" в клиенте. Этот поток узла создаст объект, а указатель интерфейса будет маршалирован обратно в свободно-потоковую квартиру клиента. Аналогичным образом, когда однопоточная апартаментная модель клиента создает многопоточный внутрипроцессный сервер, COM инициирует поток хоста в многопоточной среде (апартамент, в котором будет создан объект, а затем передан обратно в однопоточный апартамент клиента).
Заметка
Если вы разрабатываете пользовательский интерфейс COM на внутрипроцессном сервере, вам также необходимо предоставить код маршалинга для него, чтобы COM смог маршалировать интерфейс между объектами в клиентских процессах.
COM помогает защитить доступ к объектам, предоставляемым однопоточной библиотекой DLL, требуя доступа из той же клиентской квартиры, в которой они были созданы. Кроме того, все точки входа DLL (например, DllGetClassObject и DllCanUnloadNow) и глобальные данные всегда должны получать доступ одним апартаментом. COM создает такие объекты в главной квартире клиента, предоставляя главной квартире прямой доступ к указателям объекта. Звонки из других апартаментов используют межпоточное маршалирование, чтобы перейти от прокси к заглушке в главном апартаменте, а затем к объекту. Это позволяет COM синхронизировать вызовы объекта. Межпоточные вызовы медленны, поэтому рекомендуется переписать эти серверы для поддержки нескольких квартир.
Как и однопоточный процессорный сервер, объект, предоставляемый библиотекой DLL квартирной модели, должен быть доступным в том же клиентском апартаменте, где он был создан. Однако объекты, предоставляемые этим сервером, могут быть созданы в различных контекстах клиента, поэтому сервер должен реализовать свои точки входа (например, DllGetClassObject и DllCanUnloadNow) для многопоточного использования. Например, если два апартамента клиента пытаются создать два экземпляра внутрипроцессного объекта одновременно, DllGetClassObject можно вызвать одновременно из обоих апартаментов. Такой метод записи DllCanUnloadNow необходим, чтобы обеспечить, что библиотека DLL не выгружается, пока в ней выполняется код.
Если библиотека DLL предоставляет только один экземпляр класс-фабрики для создания всех объектов, реализация класс-фабрики также должна быть разработана для многопоточного использования, так как к ней будет обращаться несколько клиентских потоков. Если библиотека DLL создает новый экземпляр фабрики классов при каждом вызове DllGetClassObject, фабрика классов не должна быть потокобезопасной.
Объекты, созданные фабрикой классов, не должны быть потокобезопасны. После создания потоком объект всегда используется через этот поток, а все вызовы к объекту синхронизируются с помощью COM. Квартира, созданная как модель клиентом, создающим данный объект, получит прямую ссылку на этот объект. Клиентские квартиры, отличающиеся от квартиры, в которой был создан объект, должны получить доступ к объекту через прокси-серверы. Эти прокси-серверы создаются, когда клиент маршалирует интерфейс между своими квартирами.
Если для DLL, работающей в процессе, ThreadingModel установлено значение "Both", объект, предоставляемый этой DLL, можно создать и использовать напрямую (без прокси-сервера) в однопоточных или многопоточных клиентских апартаментах. Однако его можно использовать непосредственно в квартире, в которой она была создана. Чтобы передать объект в любую другую квартиру, объект должен быть обработан. Объект DLL должен реализовать собственную синхронизацию и может использоваться несколькими клиентскими апартаментами одновременно.
Для повышения производительности при многопоточном доступе к объектам DLL в процессах COM предоставляет функцию CoCreateFreeThreadedMarshaler. Эта функция создает свободнопоточный объект маршалинга, который можно агрегировать с внутрирпоцессным объектом сервера. Когда клиенту в той же среде выполнения требуется доступ к объекту, находящемуся в другом контексте, использование агрегированного свободно-поточного маршализатора предоставляет клиенту прямую ссылку на объект сервера, а не на прокси, если клиент маршализует интерфейс объекта для другого контекста. Клиенту не нужно выполнять синхронизацию. Это работает только в рамках одного процесса; стандартный механизм маршалинга используется для ссылки на объект, который отправляется в другой процесс.
Объект, предоставляемый внутрипроцессной библиотекой DLL, которая поддерживает только свободную многопоточность, является объектом с этой характеристикой. Она реализует собственную синхронизацию, и к ней может одновременно обращаться несколько потоков клиента. Этот сервер не передает интерфейсы между потоками, поэтому его можно создавать и использовать напрямую (без прокси) только многопоточные апартаменты в клиенте. Однопоточные квартиры, создающие его, будут получать доступ через прокси-сервер.
Связанные разделы