Шрифт:
Итак, каждый объект в СОМ+ живет в некотором контексте. Различные контексты не пересекаются друг с другом и не пересекают границы апартаментов.
Понятие контекста тесно связано с понятием перехвата. Именно механизм перехвата обеспечивает учет семантики, определенной при задании атрибутов компонента. Don Box в статье "Windows 2000 Brings Significant Refinements to the COM(+) Programming Model", Microsoft System Journal, May 1999, так описывает схему перехвата
1. Компонент описывает свои требования используя атрибуты.
2. Во время создания объекта система проверяет — выполняется ли активатор (код, вызвавший CoCreateInstance) в среде, совместимой с конфигурацией класса?
3. Если ответ на предыдущий вопрос положителен, то перехват не нужен, и CoCreateInstance возвращает прямой указатель на объект.
4. В противном случае CoCreateInstance передает управление среде, совместимой с требованиями класса, создает там объект и возвращает прокси.
5. Этот прокси обеспечивает совместимость среды выполнения с требованиями класса, выполняя определенные действия до и после каждого вызова метода.
Фактически понятие перехват появилось даже ранее MTS (Microsoft Transaction Server). В приведенную выше схему полностью укладывается процесс создания нового экземпляра класса с заданной потоковой моделью в рамках СОМ. Вообще, Don Box называет принцип перехвата краеугольным камнем современного СОМ программирования.
В СОМ маршализация указателей на интерфейс требовалась при вызове через границу апартамента. В СОМ+ такая маршализация требуется при вызове через границу контекста. В соответствии с принципом перехвата только в рамках одного контекста можно использовать прямые указатели на интерфейс.
Как и в СОМ маршализация и демаршализация указателей на интерфейс выполняется автоматически при создании, активации объекта и при вызове функций, возвращающих указатели на интерфейс. В остальных случаях, для получения указателя на интерфейс объекта из другого контекста необходимо явным образом выполнить процедуры маршализации и демаршализации указателя на интерфейс. Дня этого можно использовать функции CoMarshalInterfасе и CoUnmarshalInterfасе. Для некоторой оптимизации этого процесса можно проектировать объекты с FTM и использовать GIT, как это было в СОМ.
Синхронизация
Ранее уже упоминалаось, что при программировании в СОМ+ рекомендуется выбирать для новых классов потоковыю модель ThreadingModei = Neutral. Все экземпляры такого класса будут размещаться в одном апартаменте NA. Основное преимущество этого апартамента состоит в том, что он не имеет связанных с ним потоков, и поток из любого другого апартамента, сделавший вызов метода объекта из NA, временно покидает свой апартамент и выполняет код вызванного метода. Отсутствие переключения потоков существенно снижает затраты на вызов. Однако, необходимо побеспокоиться о синхронизации. Сам апартамент NA никак не ограничивает возможность параллельного вызова одного и того же метода одного и того же объекта из NA.
В рамках СОМ синхронизация обеспечивалась либо написанием потоко-безопасного кода (например, путем использования критических секций), либо объект помещался в STA апартамент. В СОМ+ появляется новая возможность — декларация необходимости синхронизации путем задания нужного значения для соответствующего атрибута. Сама синхронизация обеспечивается через механизм, основанный на понятии активность.
Активность — множество из нескольких контекстов, которые могут находиться не только в различных апартаментах, но и в различных процессах и даже на различных машинах. Любой контекст входит не более чем в одну активность. Могут быть контексты, не входящие ни в одну активность.
Включение контекста в некоторую активность определяется атрибутом Synchronization входящих в контекст объектов. В таблице 3.1 приведены возможные значения этого атрибута и их влияние на вхождение контекста, которому будет принадлежать активируемый объект, в ту или иную активность.
При назначении значения атрибуту Synchronization необходимо учитывать некоторые ограничения. Именно, если потоковая модель класса ThreadIngModel=Apartment, или данный класс поддерживает активацию по необходимости, или он использует сервис транзакций, необходимо выбрать для значения атрибута синхронизации либо REQUIRED, либо REQUIRES_NEW.
Теперь рассмотрим собственно механизм синхронизации. На уровне процесса активность блокируется как только поступил вызов к одному из объектов одного из ее контекстов. Только после выполнения этого вызова блокировка снимается и другой вызов может войти в активность. Для предотвращения deadlock используется отслеживание цепочек вызовов.
Каждый вызов получает уникальный идентификатор (GUID), и все вызовы, сделанные для выполнения этого вызова, получают тот же идентификатор. Вызов, идентификатор которого совпадает с идентификатором вызова, блокировавшего активность, желающий войти в активность, входит в нее.