Шрифт:
Тут все в порядке, если только инкапсулированный вызов вложен в исполняемый синхронный вызов. Если же этот инкапсулированный вызов вложен в некоторый исходящий асинхронный вызов, то могут быть проблемы. Например, в данное время в данном домене синхронизации уже может выполняться некоторый синхронный вызов и параллельное выполнение еще одного вызова противоречит логике использования домена синхронизации (?!).
Теперь настало время рассмотреть код ранее упомянутого метода HandleWorkCompletion. Как и ранее рассмотрим только ту ветвь, которая связана с обработкой синхронных вызовов.
internal virtual void HandleWorkCompletion {
Workitem nextWork = null;
bool bNotify = false;
lock (_workItemQueue) {
if (_workItemQueue.Count >= 1) {
nextWork = (Workitem) _workltemQueue.Peek;
bNotify = true;
nextWork.SetSignaled;
}
else {
_locked = false;
}
}
if (bNotify) {
if (nextWork.IsAsync) {
.......
}
else {
lock(nextWork) {
Monitor.Pulse(nextWork);
}
}
}
}
Данный метод вызывается по завершении обработки некоторой работы. В это время домен синхронизации еще блокирован (_locked == true).
Прежде всего блокируется очередь работ. Если она не пуста, мы получаем ссылку на работу (nextWork), стоящую в этой очереди первой (не извлекая ее из очереди) и устанавливаем флаг готовности данной работы к выполнению. Если же очередь пуста, домен синхронизации разблокируется (_locked = false). Это означает, что вновь пришедший синхронный вызов будет обработан сразу же без постановки в очередь.
Если в начале очереди находится синхронная работа (nextWork), то это означает, что имеется поток, который находится в состоянии ожидания того момента, когда работа nextWork будет готова к выполнению. Этот поток в свое время впал в состояние ожидания находясь в некоторой критической секции, в которую он попал заблокировав объект nextWork. Находясь в этой критической секции он был должен освободить эту блокировку, так как в то время работа nextWork была поставлена в очередь работ и этот поток не мог продолжить ее обработку. Теперь его пора разбудить. Это делает текущий поток, в котором выполняется код метода HandleWorkCompletion:
lock(nextWork) {
Monitor.Pulse(nextWork);
}
Текущий поток входит в критическую секцию (заблокировав nextWork) и уведомляет все заинтересованные потоки о том, что состояние объекта nextWork как-то изменилось. Эти находящиеся в состоянии ожидания потоки переходят в состояние готовности и по выходе текущего потока из данной критической секции пытаются захватить контроль над объектом nextWork. В нашем случае такой поток единственный и именно он ответственен за продолжение обработки вызова, инкапсулированного в работу nextWork. Конкретнее, это поток, выполняющий код метода HandieWorkRequest начиная со строки, следующей за строкой Monitor.Wait(work);
Как перехватчик обрабатывает асинхронные вызовы
Вновь возвращаемся к классу SynchronizedServerContextSink, который реализует интерфейс IMessageSink. В этом интерфейсе объявлен метод AsyncProcessMessage, который и должен реализовать обработку асинхронных вызовов в перехватчике входящих вызовов. Вот так этот метод реализован в классе SynchronizedServerContextSink:
public virtual IMessageCtrl AsyncProcessMessage(
IMessage reqMsg, IMessageSink replySink) {
Workltem work = new WorkItem (
reqMsg,
_nextSink,
replySink);
work.SetAsync;
_property.HandleWorkRequest(work);
return null;
}
В данном случае мы имеем два входных параметра. Первый задает вызов в форме сообщения типа IMessage (как и в случае синхронного вызова). А вот второй параметр специфичен для асинхронных вызовов. Он задает ссылку на еще один перехватчик, который ответственен за доставку уведомления о завершении асинхронного вызова. И, наконец, возвращаемое значение имеет также специфический для асинхронных вызовов тип — IMessageCtrl. В .NET интерфейс IMessageCtrl объявляет единственный метод Cancel, с помощью которого можно прервать выполнение асинхронного вызова. В данном случае метод AsyncProcessMessage всегда возвращает null, не предоставляя такую возможность.
Функциональность данного метода вполне понятна. Пришедший асинхронный вызов инкапсулируется в работу work, далее для нее устанавливается флаг асинхронности, позволяющий различать синхронные и асинхронные работы, и, наконец, данная работа направляется на обработку в свойство синхронизации _property.
То, как метод HandleWorkRequest обрабатывает синхронные запросы, уже было рассмотрено ранее. Теперь изучим ветви его кода, ответственные за обработку асинхронных запросов.