Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
lock (workltemQueue) {
……
}
и текущая работа помечается как ожидающая в очереди work.Setwaiting;
Потом эта работа становится в очередь работ _workltemQueue.Enqueue(work);
и, если домен синхронизации не заблокирован и данная работа в очереди единственна, инициируется ее выполнение. Для этого после установки флага готовности работы к выполнению и блокировки домена событие _asyncWorkEvent переводится в состояние signaled. Это приведет к тому,
if ((!_locked) &&
(_workItemQueue.Count == 1)) {
work.SetSignaled;
_locked = true;
_asyncWorkEvent.Set;
}
Если же очередь была не пуста, то только-что поставленная в эту очередь работа ждет своей очереди и будет извлечена из нее в свое время.
Если вызов вложенный, то инкапсулирующая его работа выполняется сразу же без постановки в очередь:
internal virtual void HandleWorkRequest(Workitem work) {
bool bQueued;
if (!IsNestedCall(work._reqMsg)) {
……
}
else {
work.SetSignaled;
work.Execute;
}
}
Возникающие при этом проблемы уже обсуждались при рассмотрении синхронного случая.
Теперь рассмотрим ту ветвь кода метода HandleWorkCompletion, которая связана с обработкой асинхроннных вызовов (в асинхронном случае этот метод будет вызван из DispatcherCallBack, который будет выполняться рабочим потоком, инициированным переводом свойства _asyncWorkEvent в состояние signaled):
internal virtual void HandleWorkCompletion {
Workltem nextWork = null;
bool bNotify = false;
lock (_workItemQueue) {
if (_workItemQueue.Count >= 1) {
nextWork = (Workltem) _workltemQueue.Peek;
bNotify = true;
nextWork.SetSignaled;
}
else {
_locked = false;
}
}
if (bNotify) {
if (nextWork.IsAsync) {
_asyncWorkEvent.Set ;
}
else {
……
}
}
}
Критическая секция
lock (workItemQueue) {
……
}
уже была рассмотрена ранее.
Пусть теперь в начале очереди находится асинхронная работа (nextWork). В этом случае событие asyncWorkEvent устанавливается в состояние signaled и на этом вся подготовка к обработке новой работы завершается.
Перехват исходящего вызова
Формирование перехватчика исходящих вызовов
Напомним, что с каждым контекстом может быть связано несколько цепочек перехватчиков. Формирование связанного со свойством синхронизации перехватчика входящих вызовов было рассмотрено в предыдущем разделе. Теперь рассмотрим формирование перехватчика исходящих вызовов.
Класс SynchronizationAttribute реализует интерфейс IContributeClientContextSink.
Благодаря этому факту, при формировании нового контекста синхронизации автоматически вызывается метод GetClientContextSink, объявленный в данном интерфейсе, который и формирует перехватчик исходящих вызовов для данного контекста.
Зачем нужен перехватчик исходящих вызовов? Предположим, контекст (домен) синхронизации реентерабельный. Это означает, что с того момента, когда поток, исполняющий некоторый вызов в данном контексте, инициировал вызов за пределы этого контекста и вошел в состоянии ожидания ответа, очередная работа может быть извлечена из очереди и может начаться выполнение инкапсулированного в ней вызова. Перехватчик исходящих вызовов как раз и замечает момент выдачи внешнего вызова и инициирует обработку очередной работы.
Ниже приводится кодметода GetClientContextSink из Rotor:
public virtual IMessageSink GetClientContextSink (
IMessageSink nextSink) {
InitlfNecessary;
SynchronizedClientContextSink propertySink =
new SynchronizedClientContextSink (
this,
nextSink);
return (IMessageSink) propertySink;
}
Этот код аналогичен коду метода GetServerContextSink, в связи с чем комментарии опущены.
Как и в случае перехватчика входящих вызовов, при одном на весь домен синхронизации свойстве синхронизации, для каждого контекста в этом домене формируется свой перехватчик исходящих вызовов, имеющий ссылку на это свойство синхронизации.
Класс SynchronizedClientContextSink наследует классу InternalSink и реализует интерфейс IMessageSink. Его основная функциональность определяется двумя методами интерфейса IMessageSink: SyncProcessMessage и AsyncProcessMessage, обрабатывающими соответственно синхронные и асинхронные исходящие вызовы.
Перехват исходящих синхронных вызовов
Случай реентерабельного контекста
Начнем со случая реентерабельного контекста (домена). Вот соответствующая ветвь кода метода SyncProcessMessage:
public virtual IMessage SyncProcessMessage(
IMessage reqMsg) {
IMessage repiyMsg;
if (_property.IsReEntrant) {
_property.HandleThreadExit;