Шрифт:
Как свойство синхронизации обрабатывает инкапсулированный асинхронный вызов, полученный от перехватчика
Ниже приведена часть кода HandleWorkRequest, которая относится к обработке именно асинхронных вызовов:
internal virtual void HandieWorkRequest(Workltem work) {
bool bQueued;
if (!IsNestedCall(work._reqMsg)) {
if (work.IsAsync) {
bQueued = true;
lock (workltemQueue) {
work.SetWaiting;
_workltemQueue.Enqueue(work);
if ((!_locked) & &
(_workItemQueue.Count == 1)) {
work.SetSignaled;
_locked = true;
_asyncWorkEvent.Set;
}
}
}
else {
…..
}
}
else {
work.SetSignaled;
work.Execute;
}
}
Итак, если вызов не вложенный, происходит следующее. Блокируется очередь работ (текущий поток входит в критическую секцию)
lock (workltemQueue) {
……
}
и текущая работа помечается как ожидающая в очереди work.Setwaiting;
Потом эта работа становится в очередь работ _workltemQueue.Enqueue(work);
и, если домен синхронизации не заблокирован и данная работа в очереди единственна, инициируется ее выполнение. Для этого после установки флага готовности работы к выполнению и блокировки домена событие _asyncWorkEvent переводится в состояние signaled. Это приведет к тому, что свободный рабочий поток из пула потоков начнет выполнять метод DispatcherCallBack, в процессе чего данная работа и будет извлечена из очереди и отправлена на выполнение:
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, объявленный в данном интерфейсе, который и формирует перехватчик исходящих вызовов для данного контекста.