Шрифт:
Интервал:
Закладка:
Теперь обратимся к методу GetPropertiesForNewContext:
public override void GetPropertiesForNewContext {
IConstructionCallMessage ctorMsg) {
if ((_flavor==NOT_SUPPORTED) || (_flavor==SUPPORTED) ||
(null == ctorMsg)) {
return;
}
if (_cliCtxAttr!= null) {
ctorMsg.ContextProperties.Add(
(IContextProperty)_cliCtxAttr);
_cliCtxAttr = null;
}
else {
ctorMsg.ContextProperties.Add((IContextProperty)this);
}
}
Метод GetPropertiesForNewContext вызывается системой в том случае, когда старый контекст не пригоден для жизни нового объекта.
Единственный аргумент ctorMsg типа IConstructionCallMessage должен быть сообщением, передаваемым со стороны клиента на сторону сервера и содержащим необходимую информацию об активируемом объекте. Роль рассматриваемого метода состоит в добавлении в это сообщение дополнительной информации. Именно, добавляется ссылка на объект типа IContextProperty, который будет играть роль свойства синхронизации нового контекста (свойство типа SynchronizationAttribute с именем "Synchronization").
Из приведенного кода видно, что исходное сообщение ctorMsg никак не меняется, если при задании атрибута был выбран флаг not_supported или supported, иными словами, если активируемый объект не должен жить в контексте синхронизации или разработчику все равно.
В противном случае возможны два варианта:
• _cliCtxAttr == null
Этот случай возникает, когда либо старый контекст не поддерживает сервис синхронизации, либо атрибут синхронизации был задан с флагом REQUIRES_NEW. В этом случае в качестве ссылки на свойство синхронизации в сообщение ctorMsg включается ссылка на новый экземпляр атрибута синхронизации, который активируется системой еще до активации нового объекта. Таким образом получается новый контекст синхронизации, никак не связанный с каким-либо из ранее созданных контекстов синхронизации. Этот новый контекст синхронизации образует и новый домен синхронизации.
• _cliCtxAttr!= null
В этом случае cliCtxAttr является ссылкой на свойство синхронизации старого контекста и именно эта ссылка включается в сообщение ctorMsg как ссылка на свойство синхронизации нового контекста. Таким образом оба контекста синхронизации (старый и новый) имеют одно и то же свойство синхронизации. В этом случае говорят, что оба контекста принадлежат одному домену синхронизации.
Теперь уместно отметить одну особенность домена синхронизации, связанную с реентерабельностью. Предположим, что активируется экземпляр o1 некоторого класса, описанного с атрибутом
[Synchronization(REQUIRIES_NEW, true)]}.
В этом случае формируется новый контекст синхронизации, который начинает собой и новый домен синхронизации. До активации объекта o1 активируется новый экземпляр атрибута синхронизации. В процессе выполнения его конструктора в поле _bReEntrant атрибута синхронизации сохраняется значение true. Таким образом, созданный контекст синхронизации является реентерабельным контекстом.
Предположим теперь, что при выполнении некоторого метода объекта o1 активируется экземпляр о2 некоторого класса, описанного с атрибутом [synchronization ()]. Это означает, что объект о2 должен жить в контексте синхронизации (флаг required), но не допускается реентерабельность. В зависимости от других атрибутов, приписанных классам, экземплярами которых являются объекты o1 и о2, эти объекты будут жить в одном контексте, либо в различных контекстах, но в одном домене синхронизации. Это определяется тем, что реентерабельность никак не учитывается при определении границ домена синхронизации. В связи с тем, что все контексты одного домена синхронизации имеют одно на всех свойство контекста с именем "Synchronization", а это свойство в рассматриваемом примере разрешает реентерабельность, объект о2 против желания его разработчика оказывается в реентерабельном контексте, что может привести к ошибке.
Аналогичная проблема возникает, когда первый контекст домена синхронизации формируется под объект, который не должен жить в реентерабельном контексте. В этом случае весь домен синхронизации будет нереентерабельным, и работа с живущими в нем реентерабельными объектами будет менее эффективной, чем это могло бы быть при активации реентерабельных объектов в реентерабельном контексте.
Из этого анализа следует, что в методы IsContextOK и GetPropertiesForNewContext нужно внести изменения, касающиеся реентерабельности. В результате в один домен синхронизации должны помещаться только реентерабельные, либо только нереентерабельные объекты.
Теперь остановимся на том сервисе, который обеспечивает свойство контекста синхронизации (свойство синхронизации). Изложение носит описательный характер, но оно основано на изучении кода атрибута синхронизации.
При поступлении вызова в домен синхронизации (в форме сообщения типа IMessage) это сообщение перехватывается перехватчиком входящих вызовов, определенном в свойстве синхронизации данного домена синхронизации.
Вызов может быть как синхронным, так и асинхронным. В обоих случаях перехватчик входящих вызовов формирует соответствующую данному вызову работу (экземпляр класса WorkItem), в которой сохраняется:
• Само сообщение
• Идентификатор контекста (данного домена синхронизации), в который пришел вызов
• Контекст логического вызова
• Ссылка на перехватчик входящих вызовов, которому нужно передать сообщение для дальнейшей обработки
• Перехватчик результатов, на который нужно послать результат (для асинхронного вызова)
Дальнейшая судьба работы определяется ее типом и наличием в домене синхронизации других работ, ожидающих выполнения:
• Если домен синхронизации нереентерабельный и работа представляет собой вложенный вызов (для синхронного вызова, который сейчас выполняется в данном домене), то данная работа сразу же выполняется без какого-либо ожидания. Заведомо известно, что в данный момент в данном домене синхронизации не выполняется ни один другой поток.
• Если вызов синхронный и не вложенный, а очередь работ, поддерживаемая свойством синхронизации данного домена, пуста и домен синхронизации не блокирован, то такой вызов также сразу же начинает выполняться. При этом домен блокируется для выполнения каких-либо других работ (кроме вложенных вызовов). Все вновь пришедшие во время блокировки домена работы записываются в очередь.
• Асинхронный вызов всегда записывается в очередь (даже если она пуста и домен не блокирован).
Работы извлекаются из очереди, если в домене нет исполняемой работы (например, предыдущая работа завершена).
В случае синхронного вызова на всех этапах как до постановки соответствующей работы в очередь, так и после ее извлечения из очереди выполняется один и тот же поток. В случае асинхронного вызова при извлечении работы из очереди выполняется выделенный системой свободный рабочий поток из пула рабочих потоков.
Если в процессе выполнения вызова делается новый вызов из контекста синхронизации за его пределы, этот вызов перехватывается перехватчиком исходящих вызовов. В случае реентерабельного домена синхронизации этот перехватчик уведомляет свойство синхронизации о том, что домен свободен для выполнения новой работы из очереди работ или вновь пришедшего вызова в случае пустой очереди.
Важный практический вывод. Различие между синхронными и асинхронными вызовами состоит в том, что в случае синхронного вызова клиент блокируется в ожидании ответа, а в случае асинхронного не ожидает ответа (его уведомление о результате вызова возлагается на специальный перехватчик). Таким образом разработчик может надеяться на то, что синхронные вызовы могут обрабатываться в домене синхронизации