S>CompareExchange работает примерно так
S>S>lock(field)
S>{
S> return field == current?
S> (field = newValue) : // мозг спит, полноценный if-else писать лень.
S> field
S>}
S>
Согласно
MSDN код должен работать так:
lock(field)
{
return field == newValue?
(field = current) :
field
}
Наверное параметры newValue и curent нужно поменять местами:
public static T Modify<T>(ref object field, Func<T, T> selector) where T: class
{
T current;
T newValue;
do
{
current = (T)Thread.VolatileRead(ref field);
newValue = selector(current);
}
while (object.ReferenceEquals(
Interlocked.CompareExchange(ref field, newValue, current),
current));
return newValue;
}
условие в while работает так: если field == current (то есть в field старое значение (не модифицированное через selector)), то в field записываем newValue. Если field != current, то ничего не делаем. CompareExchange возвращает исходное значение field в независимости от того было ли выполнено присвоение newValue в filed. Если ReferenceEquals возвращает истину, то считается, что field не изменилось (что возможно из-за многопоточности), и нужно заново попробовать присвоить newValue в field. Если ReferenceEquals возвращает ложь (то есть field != current), то считается что field успешно модифицировалось и можно завершать цикл.
Есть вопросы:
1)А если selector никак не изменяет field? метод Modify зациклится?
2)То что field != current (см ReferenceEquals) не означает что field == newValue (из-за многопоточности). То есть как я понимаю метод Modify не гарантирует, что после его выполнения field == selector(filed).
3)Предположим ReferenceEquals возвратил ложь. А если после выполнения ReferenceEquals и до return другой поток опять присвоит field старое значение?
4) А если selector не детерминированный? То есть например зависит от текущего времени?
Поправьте, если не так. Возможно я неправильно понимаю область применения метода Modify.
Здравствуйте, igor-booch, Вы писали:
IB>Наверное параметры newValue и curent нужно поменять местами:
Угу, огромное спасибо что поправили!
Самое интересное, что в рабочий код ушёл правильный вариант, по крайней мере в логе свн-а правок нет
[SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#",
Justification = CodeAnalysisConstants.CA1045RefIntended)]
public static T Modify<T>(ref T field, Func<T, T> selector) where T: class
{
T result;
T prevValue;
T valueBeforeExchange = field;
do
{
prevValue = valueBeforeExchange;
result = selector(prevValue);
valueBeforeExchange = Interlocked.CompareExchange(ref field, result, prevValue);
}
while (valueBeforeExchange != prevValue);
return result;
}
IB>1)А если selector никак не изменяет field? метод Modify зациклится?
Как видно из кода выше — нет
IB>2)То что field != current (см ReferenceEquals) не означает что field == newValue (из-за многопоточности). То есть как я понимаю метод Modify не гарантирует, что после его выполнения field == selector(filed).
Да, задача — безопасно обновить значение поля. Если кто-то будет работать с полем напрямую, магия выше, естественно, не спасёт.
IB>3)Предположим ReferenceEquals возвратил ложь. А если после выполнения ReferenceEquals и до return другой поток опять присвоит field старое значение?
IB>4) А если selector не детерминированный? То есть например зависит от текущего времени?
Ничего страшного. Наша задача — обновить именно то значение, которое мы передали в selector.