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