Re[3]: [Compare-exchange loop] Как бы сделали вы?
От: igor-booch Россия  
Дата: 07.06.11 09:49
Оценка: 27 (1)
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.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[4]: [Compare-exchange loop] Как бы сделали вы?
От: Sinix  
Дата: 07.06.11 12:00
Оценка:
Здравствуйте, 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.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.