Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
• Реентерабельность
Предположим, что домен синхронизации блокирован на время выполнения вызова X. Пусть в процессе выполнения X был сделан вызов X1 за пределы домена синхронизации. Обычно ни один поток не может выполнить какой-либо работы в данном домене синхронизации пока не будет получен ответ или не придет вложенный вызов для вызова xi. Однако в ряде случаев можно разрешить принимать во время ожидания другие внешние вызовы, никак не связанные с вызовом X. В этом случае домен синхронизации называется реентерабельным (reentrant).
Теперь обратимся к конструктору класса SynchronizationAttribute. Для этого класса имеется четыре конструктора. Рассмотрим прежде четвертый конструктор, т. к. первые три делегируют вызов четвертому.
public SynchronizationAttribute(int flag, bool reEntrant)
: base(PROPERTY_NAME) {
_bReEntrant = reEntrant;
switch (flag) {
case NOT_SUPPORTED:
case SUPPORTED:
case REQUIRED:
case REQUIRES_NEW:
_flavor = flag;
break;
default:
throw new ArgumentException(
Environment.GetResourceString(
"Argument_InvalidFlag"),
"flag");
}
}
Первый параметр flag принимает одно из четырех возможных значений:
• NOT_SUPPORTED (== 0x00000001)
Данный флаг означает, что экземпляр класса, которому приписан атрибут синхронизации с соответствующим флагом в конструкторе, не должен активироваться в каком-либо контексте синхронизации.
• SUPPORTED (== 0x00000002)
Данный флаг означает, что разработчик не заботится о том, в каком именно контексте (синхронизации или нет) будет активирован экземпляр его класса.
• REQUIRED (== 0x00000004)
Данный флаг означает, что экземпляр класса с соответствующим атрибутом всегда должен активироваться в контексте синхронизации.
• REQUIRES_NEW (== 0x00000008)
Данный флаг означает необходимость создания нового контекста синхронизации для нового экземпляра класса.
Если аргумент flag содержит какое-либо иное значение, генерируется соответствующее исключение.
Второй логический аргумент равен true, если экземпляр класса, которому приписан данный атрибут, может жить в реентерабельном контексте. В противном случае второй аргумент равен false.
В конструкторе класса SynchronizationAttribute вызывается конструктор базового класса ContextAttribute с аргументом property_name (строковой константой равной "Synchronization"). Именно так называется свойство синхронизации в SSCLI.
Остальные конструкторы определяются через рассмотренный выше:
public SynchronizationAttribute
: this(REQUIRED, false) {}
public SynchronizationAttribute(bool reEntrant)
: this(REQUIRED, reEntrant) {}
public SynchronizationAttribute(int flag)
: this(flag, false) {}
Теперь рассмотрим важнейший для правильного понимания и использования атрибута синхронизации вопрос — реализацию методов IsContextOK и GetPropertiesForNewContext.
Начнем
с виртуального метода IsContextOK, объявленного в интерфейсе IContextAttribute и реализованного в классе ContextAttribute. В рассматриваемом коде из SSCLI этот метод переопределяется следующим образом:public override bool IsContextOK(Context ctx,
IConstructionCallMessage msg) {
if (ctx == null)
throw new ArgumentNullException("ctx");
if (msg == null)
throw new ArgumentNullException("msg");
bool isOK = true;
if (_flavor == REQUIRES_NEW) {
isOK = false;
}
else {
SynchronizationAttribute syncProp =
(SynchronizationAttribute) ctx.GetProperty(PROPERTY_NAME);
if (((_flavor == NOT_SUPPORTED)&&(syncProp!= null))
|| ((_flavor == REQUIRED)&&(syncProp == null))
) {
sOK = false;
}
if (_flavor == REQUIRED) {
_cliCtxAttr = syncProp;
}
}
return isOK;
}
Таким образом контекст ctx признается непригодным для жизни экземпляра класса описанного с атрибутом синхронизации в следующих случаях:
1. В конструкторе атрибута был задан флаг REQUIRES_NEW
2. В контексте ctx имеется свойство контекста с именем "Synchronization" типа SynchronizationAttribute, а в конструкторе атрибута был явно задан флаг NOT_SUPPORTED
3. В контексте ctx нет свойства контекста с именем "Synchronization" типа SynchronizationAttribute, но при вызове конструктора атрибута был выбран (явно или неявно) флаг required.
Во всех остальных случаях возвращается true, т. е. заданный контекст признается пригодным для жизни объекта.
Эти правила определяют условия размещения нового объекта в старом контексте или необходимость формирования нового контекста. Однако остается вопрос о домене синхронизации. Когда новый контекст, если он был построен, будет включен в тот домен синхронизации, в который входит контекст ctx?
Обратим внимание на строки
if (_flavor == REQUIRED) {
_cliCtxAttr = syncProp;
}
Переменная syncProp равна null или ссылке на свойство контекста ctx с именем "Synchronization" типа SynchronizationAttribute. Таким образом, при заданном флаге REQUIRED в поле _cliCtxAttr типа SynchronizationAttribute в текущем экземпляре атрибута синхронизации сохраняется ссылка на одноименное свойство контекста ctx. Это подготовка к включению нового контекста (если он понадобится) в домен синхронизации, в который уже входит контекст ctx. Подробнее этот вопрос будет изложен при комментировании кода метода GetPropertiesForNewContext.