Шрифт:
Интервал:
Закладка:
Table: Соответствие потоковой модели класса и допустимого типа апартамента
Потоковая модель ∙ Тип апартамента
Нет ∙ Главный STA
Apartment ∙ Любой STA
Free ∙ МТА
Both ∙ Любой STA или МТА
Neutral ∙ NA
Отметим несколько моментов, связанных с этой таблицей.
Значение Нет соответствует случаю класса, который создавался до внедрения многопоточности в Windows. Все экземпляры таких классов помещаются в главный STA. В связи с тем, что с этим апартаментом связан только один поток, методы всех объектов, живущих в данном апартаменте, не могут выполняться параллельно. Это гарантирует безопасность работы с такими объектами в многопотоковой среде. Но эта безопасность достигается за счет неизбежного снижения производительности системы. Чем больше объектов живет в главном STA, тем дольше приходится клиенту ожидать выполнения вызова метода живущего в этом апартаменте объекта.
Значение Apartment означает, что экземпляр данного класса не рассчитывает на параллельный вызов его методов, хотя сам обращается к глобальным переменным (например, счетчикам числа активированных объектов) безопасным образом. Объкт, поддерживающий данную модель, будет размещен в STA. Если создавший данный объект поток сам живет в STA, то именно в этом STA и будет жить новый объект. Если же этот поток принадлежит МТА, то будет создан новый STA (и новый связанный с ним поток), в который и будет помещен созданный объект. Данная модель позволяет параллельно обращаться к объектам, живущим в различных STA. Это существенное улучшение по сравнению с использованием одного главного STA.
Значение Free означает, что экземпляр данного класса знает о потоках все и готов к получению параллельных вызовов своих методов. Такой объект помещается в МТА (МТА создается, если его ранее не было).
Значение Both означает, что экземпляр данного класса не только готов к получению параллельных вызовов своих методов, но и осторожен в обращении со своими клиентами. При взаимодействии двух объектов разделение их по признаку клиент/сервер достаточно условно. Вызываемый объект в свою очередь может вызвать какие-то методы вызывающего объекта. Разделяя потоко-безопасные и потоко-опасные объекты по различным апартаментам (МТА и STA) мы обеспечиваем, кроме всего прочего, защиту вызывающего объекта от вызываемого.
Но для общения через границы апартаментов нужны дополнительные ресурсы. Было бы оптимально поместить вызываемый объект в апартамент вызывающего объекта, что обеспечит прямой доступ к его методам. Если потоковая модель класса объявлена как Both, это означает гарантию со стороны разработчика класса того, что экземпляр данного класса будет корректно работать как с потоко-безопасными объектами в МТА, так и с объектами не поддерживающими параллельный доступ, в STA. Это позволяет разместить данный объект в том апартаменте, в котором живет создавший его поток. Тем самым обеспечивается максимальная эффективность доступа к методам данного объекта из создавшего его потока.
Значение Neutral требует размещения экземпляра класса в апартаменте NA.
Таблица 2.2 суммирует сделанные выше замечания. В первом столбце указывается тип апартамента, в котором живет поток, создавший объект. В остальных столбцах указывается апартамент, в который будет помещен создаваемый объект в зависимости от значения параметра ThreadingModei, приписанного соответствующему классу в реестре системы.
Итак, мы рассмотрели типы апартаментов и условия, определяющие привязку потоков к апартаментам и размещение создаваемых объектов в апартаментах. Теперь надо разобраться с тем, как можно вызывать объекты из потоков, которые могут принадлежать как одному, так и различным апартаментам.
Проще всего ситуация, когда и вызывающий поток и вызываемый объект находятся в одном апартаменте, причем поток, создавший этот объект, находится в этом же апартаменте
• В случае STA
поток имеет прямой указатель на некоторый интерфейс объекта (т. к. именно этот поток и создал этот объект) и может делать прямые вызовы его методов.
• В случае МТА
♦ либо данный поток создал данный объект и, следовательно, имеет на него прямой указатель,
♦ либо объект создан каким-то другим потоком из МТА.
В этом случае поток, создавший объект, может сохранить указатель на его интерфейс в глобальной переменной. Все остальные потоки из МТА (и только из МТА!) могут использовать этот указатель для прямого вызова методов данного интерфейса.
Напрямую можно обращаться и к объектам из NA, независимо от того, поток из какого апартамента создал этот объект. Эта новая технология появилась в Windows 2000. Рекомендуется все вновь создаваемые СОМ+ объекты создавать для размещения их в NA. Преимуществом данной технологии является отсутствие переключения потоков. Вызывающий поток независимо от апартамента покидает свой апартамент и выполняет код метода объекта из NA. В данном разделе мы более не будем обсуждать эти новые возможности и продолжим изучать проблемы, связанные с классическими STA и МТА.
Ранее уже не однократно делались намеки на то, что если вызывающий поток и вызываемый объект живут в разных апартаментах, то такой вызов обходится дороже, чем прямой вызов в случае, когда и поток и объект живут в одном апартаменте.
В чем причина? Вызов через границы апартаментов выполняется через пару прокси/стаб. Прокси находится в апартаменте вызывающего процесса, а стаб в апартаменте вызываемого объекта.
Зачем нужны эти прокси и стаб? До сих пор они упоминались как средство, обеспечивающее маршалинг вызовов методов через границу между процессами или между машинами. Но в данном случае и вызывающий поток, и вызываемый объект находятся в одном адресном пространстве. Ответ состоит в следующем. Прямой вызов через границы апартаментов запрещен, т. к. иначе возможно параллельное обращение к объекту, который не поддерживает параллельные вызовы своих методов. Прокси и стаб обеспечивают безопасный доступ к объекту из другого апартамента.
Здесь уместно заметить, что прокси и стаб должны быть зарегистрированы в реестре как поддерживающие потоковую модель Both. Это позволит загружать прокси и стаб в любой апартамент. Такая регистрация выполняется автоматически, если используется idl для описания интерфейсов и компилятор MIDL.
Прежде чем разбирать различные возможные сценарии вызова метода объекта, рассмотрим вопрос о том, как вызывающий поток получает указатель на интерфейс вызываемого объекта, живущего в другом апартаменте. Возможны две возможности:
• Данный поток сам инициировал порождение данного объекта
В этом случае автоматически выполняется маршалинг запрошенного интерфейса, который сводится к получению некоторого нейтрального к апартаменту представления указателя на интерфейс, передачи