chitay-knigi.com » Разная литература » Интернет-журнал "Домашняя лаборатория", 2007 №6 - Усманов

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 251 252 253 254 255 256 257 258 259 ... 361
Перейти на страницу:
его свойства LogicalCallID.

Если в данный момент не выполняется синхронный вызов, или новый вызов не является вложенным для исполняемого синхронного вызова, то начинается проверка вложенности нового вызова в один из исходящих асинхронных вызовов.

Выполнение условия AsyncCallOutLCIDList.count>0 означает, что список идентификаторов логических вызовов, соответствующих исходящим асинхронным вызовам, не пуст. Свойство AsyncCallOutLCIDList типа ArrayList возвращает ссылку на этот список.

В этом случае как и ранее выясняется контекст вызова reqMsg и проверяется, что соответствующий ему идентификатор логического вызова содержится в списке исходящих асинхронных вызовов. В случае успеха флаг bNested получает значение true:

LogicalCallContext callCtx =

      (LogicalCallContext)

            reqMsg.Properties[Message.CallContextKey];

if (AsyncCallOutLCIDList.Contains(

        callCtx.RemotingData.LogicalCalllD)) {

        bNested = true;

}

Возвращаемся вновь к коду метода HandleWorkRequest, который мы рассматриваем в данный момент только для случая инкапсулированного синхронного вызова.

Здесь возможны два случая:

• IsNestedCall(work._reqMsg) == false

Это случай соответствует реентерабельному контексту или невложенному вызову.

В случае синхронности инкапсулированного вызова его нужно поставить в очередь (или выполнять без постановки в очередь, если в данный момент времени в данном домене синхронизации не выполняется ни один поток и очередь работ пуста).

 IsNestedCall(work._reqMsg) == true

А вот в этом случае при синхронности инкапсулированного в work вызова и его вложенности в исполняемый синхронный вызов инкапсулированный вызов следует выполнять сразу же без постановки в очередь, так как его выполнения ожидает основной выполняемый в данный момент синхронный вызов.

Другая возможность — инкапсулированный вызов является вложенным для исходящего асинхронного вызова. Но можно ли этот инкапсулированный вызов исполнять вне очереди и в этом случае? Отложим обсуждение этого вопроса.

Итак, в первом случае (не вложенный вызов), поток входит в критическую секцию

lock(work) {

   ……

}

заблокировав доступ к работе work, инкапсулирующей входящий вызов. В рамках этой критической секции поток ожидает входа в критическую секцию, для доступа к которой надо заблокировать очередь работ:

lock(_workltemQueue) {

    ……

}

Войдя в эту вторую критическую секцию, поток проверяет — имеется ли блокировка домена синхронизации. Поле _locked атрибута синхронизации равно false в том случае, когда после выполнения очередной работы обнаруживается, что очередь работ пуста, то есть нет работы, которую следует начинать выполнять в рамках данного домена синхронизации.

Если домен синхронизации не заблокирован, и очередь работ пуста, то домен блокируется, но флаг постановки работы в очередь bQueued не задается (работа может быть выполнена сразу же):

if ((!_locked) &&

     (_workltemQueue.Count == 0)) {

      _locked = true;

      bQueued = false;

}

В противном случае задается флаг bQueued постановки работы в очередь, в самой работе задается флаг, указывающий на то, что она стоит в очереди (work.SetWaiting ()) и выполняется реальная запись работы в очередь:

else {

      bQueued = true;

      work.SetWaiting();

       _workltemQueue.Enqueue(work);

}

Теперь поток выходит из второй (внутренней) критической секции, разблокировав очередь работ. Но что ему делать дальше?

Если работа была поставлена в очередь, то в силу синхронности вызова данный поток должен сам перейти в состояние ожидания, из которого он выйдет только тогда, когда только что поставленная в очередь работа продвинется в ее начало и будет готова для выполнения:

if (bQueued == true) {

    Monitor.Wait(work);

    if (!work.IsDummy()) {

           DispatcherCaiiBack(null, true);

     }

     else {

           lock(workltemQueue) {

                _workltemQueue.Dequeue();

             }

      }

}

Вызов Monitor.Wait (work) переведет текущий поток в состояние ожидания, причем этот поток освободит ранее заблокированную им работу work и будет ожидать сигнала, говорящего о том, что состояние объекта work изменилось, нужно проснуться и продолжить работу с этим объектом. Это сигнал будет выдан другим потоком, заметившим, что работа work первая в очереди и нет препятствий для ее выполнения.

Следующая за Monitor.Wait(work); строка кода будет выполняться уже разбуженным потоком, который вновь получает исключительный доступ к объекту work. Если данная работа не является работой-заглушкой (об этом позже), то вызывается уже рассмотренный метод DispatcherCallBack, который и извлечет эту работу из очереди, выполнит ее и инициирует выполнение следующей работы. В случае же работы-заглушки просто блокируется очередь работ и эта работа-заглушка удаляется из очереди.

А вот что происходит с работой, которую не пришлось ставить в очередь:

else {

     if (!work.IsDummy()) {

          work.SetSignaled();

          ExecuteWorkltem(work);

          HandleWorkCompletion();

     }

}

Если это не заглушка, устанавливается флаг готовности к выполнению, работа выполняется и потом все подготавливается для выполнения следующей работы.

Здесь надо бы рассмотреть код для метода HandleWorkCompletion(), который собственно и подготавливает все для выполнения следующей работы, но в очередной раз отложим это рассмотрение на потом.

Теперь рассмотрим второй случай, когда вызов синхронный вложенный, а контекст нереентерабельный. Такой вызов надо выполнять вне очереди:

if (!IsNestedCall(work._reqMsg)) {

}

else {

     work.SetSignaled();

     work.Execute();

}

Тут устанавливается флаг готовности к выполнению, работа выполняется, но никакой подготовки к выполнению следующей работы не проводится, т. к. уже имеется находящийся в состоянии выполнения вызов, ожидающий выполнения данного синхронного вызова.

Тут все в порядке, если только инкапсулированный вызов вложен в исполняемый синхронный вызов. Если же этот инкапсулированный вызов вложен в некоторый исходящий асинхронный вызов, то могут быть проблемы. Например, в данное время в данном домене синхронизации уже может выполняться некоторый синхронный вызов и параллельное выполнение еще одного вызова противоречит логике использования домена синхронизации (?!).

Теперь настало время рассмотреть код ранее упомянутого метода 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);

                       }

                 }

         

1 ... 251 252 253 254 255 256 257 258 259 ... 361
Перейти на страницу:

Комментарии
Минимальная длина комментария - 25 символов.
Комментариев еще нет. Будьте первым.