Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
По завершении вызова метода Add клиент получает из свойства контекста значение свойства serverName, добавленное в контекст вызова сервером при выполнении Add. В результате клиент получает информацию о сборке, содержащей код сервера, выполнившего вызов.
Результаты
Ниже приведен вывод на консоль сервера. Видно, что сервер получил информацию о том, что клиентское приложение было запущено Администратором.
Server is listening
UserName = Администратор
Bye
А
5 is sent to MyServer, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null
Total = 5
Bye
Возвращаемся к классу Workltem
Конструктор
Конструктор в качестве входных параметров принимает вызов в форме сообщения (reqMsg), ссылку на следующий перехватчик, которому будет переадресован данный вызов после того, как он отстоит всю очередь (nextSink) и ссылку на перехватчик для результатов асинхронного вызова (replySink). В случае синхронного вызова последний параметр задается равным null.
Ниже приведен код конструктора:
internal Workltem(IMessage reqMsg, IMessageSink nextSink,
IMessageSink replySink) {
_reqMsg = reqMsg;
_replyMsg = null;
_nextSink = nextsink;
_replySink = replySink;
_ctx = Thread.CurrentContext;
_callCtx = CallContext.GetLogicalCallContext;
}
Судя по приведенному коду, этот конструктор вызывается в том контексте, в котором в последствии будет выполняться вызов, инкапсулируемый в данный момент в экземпляр класса WorkItem. Об этом говорят строки, в которых присваиваются значения полям ctx и _callContext. Вызов Thread.CurrentContext возвращает текущий контекст (ссылку на экземпляр класса Context), а вызов CallContext.GetLogicalCallContext возвращает контекст логического вызова (ссылку на экземпляр класса LogicalCallCcontext), соответствующие текущим контексту и потоку. Здесь следует отметить, что в .NET Framework класс CallContext не реализует метод GetLogicalCallContext.
Итак, конструктор класса WorkItem должен вызываться в перехватчике, ассоциированном со свойством синхронизации, и этот перехватчик должен выполняться в том контексте и в потоке с таким контекстом вызова, в котором и с которым будет выполняться сам перехваченный вызов.
Флаги
В течении своей жизни работа, представляющая некоторый вызов, может находиться в одном из нескольких состояний. Эти состояния задаются наборами флагов:
private const int FLG_WAITING = 0x0001;
private const int FLG_SIGNALED = 0x0002;
private const int FLG_ASYNC = 0x0004;
private const int FLG_DUMMY = 0x0008;
Флаг FLGg_WAITING означает, что работа поставлена в очередь, флаг FLG_SIGNALED указывает на то, что первая в очереди работа начинает исполняться, флаг FLG_ASUNC помечает асинхронные работы (работы, представляющие асинхронные вызовы), и, наконец, флаг FLG_DUMMY помечает работу-заглушку. Этот флаг помечает фиктивную работу,
формируемую для представления результата внешнего вызова, сделанного при выполнении текущего вызова. Подробнее об этом будет говориться далее.
Текущая комбинация флагов сохраняется в поле internal int flags, для задания и чтения которого используются следующие методы:
internal virtual void SetWaiting {
_flags |= FLG_WAITING;
}
internal virtual bool IsWaiting {
return (_flags &FLG_WAITING) == FLG_WAITING;
}
internal virtual void SetSignaled {
_flags |= FLG_SIGNALED;
}
internal virtual bool IsSignaled {
return (_flags & FLG_SIGNALED) == FLG_SIGNALED;
}
internal virtual void SetAsync {
_flags |= FLG_ASYNC;
}
internal virtual bool IsAsync {
return (_flags & FLG_ASYNC) == FLG_ASYNC;
}
internal virtual void SetDummy {
_flags |= FLG_DUMMY;
}
internal virtual bool IsDummy {
return (_flags & FLG_DUMMY) == FLG_DUMMY;
}
Выполнение работы
Самый важный метод класса WorkItem — это метод Execute, обеспечивающий выполнение текущей работы. Этот метод вызывается в тот момент, когда подошла очередь выполнения этой работы.
internal virtual void Execute {
ContextTransitionFrame frame = new ContextTransitionFrame;
Thread.CurrentThread.EnterContext(_ctx, ref frame);
LogicalCallContext oldCallCtx =
CallContext.SetLogicalCallContext(_callCtx);
if (IsAsync) {
_nextSink.AsyncProcessMessage(_reqMsg, _replySink);
}
else if (_nextSink!= null) {
_replyMsg = _nextSink.SyncProcessMessage(_reqMsg);
}
CallContext.SetLogicalCallContext(oldCallCtx);
Thread.CurrentThread.ReturnToContext(ref frame);
}
Метод Execute может выполняться в различных контекстах и потоках, поэтому прежде всего нужно восстановить ту среду, в которой находился вызов в момент его перехвата и инкапсулирования в работу типа WorkItem.
Для этого формируется фрейм, сохраняющий некоторую информацию о переходе их одного контекста в другой контекст (эта информация позже используется для возвращения в контекст, в котором началось исполнение метода Execute) и выполняется переход из текущего контекста в контекст, сохраненный в поле ctx:
ContextTransitionFrame frame = new ContextTransitionFrame;
Thread.CurrentThread.EnterContext(_ctx, ref frame);
Отметим, что в .NET Framework нет класса ContextTransitionFrame, а в сигнатуре класса Thread нет метода EnterContext.