Зачем вам нужен SpinWait в образце LockFreeUpdate из "Потоки в C#" Албахари
Я работаю над малым Ref<T>
абстракция с оптимистичным методом обновления без блокировок (предполагается работать с неизменяемым T
структуры данных, в частности неизменные деревья). Он основан на "Threading в C#" Албахари LockFreeUpdate
метод в разделе использования SpinWait:
static void LockFreeUpdate<T> (ref T field, Func <T, T> updateFunction) where T : class
{
var spinWait = new SpinWait();
while (true)
{
T snapshot1 = field;
T calc = updateFunction (snapshot1);
T snapshot2 = Interlocked.CompareExchange (ref field, calc, snapshot1);
if (snapshot1 == snapshot2) return;
spinWait.SpinOnce();
}
}
Мой вопрос: зачем нам нужен SpinWait? Потому что, если поле уже обновлено, другой параллельный код может немедленно повторить попытку обновления с новым значением.
Полный код ссылки без SpinWait указан ниже (проверьте Update
метод для сравнения):
public sealed class Ref<T> where T : class
{
public T Value { get { return _value; } }
public Ref(T initialValue = default(T))
{
_value = initialValue;
}
public T Update(Func<T, T> update)
{
var retryCount = 0;
while (true)
{
var oldValue = _value;
var newValue = update(oldValue);
if (Interlocked.CompareExchange(ref _value, newValue, oldValue) == oldValue)
return oldValue;
if (++retryCount > RETRY_COUNT_UNTIL_THROW)
throw new InvalidOperationException(ERROR_EXCEEDED_RETRY_COUNT);
}
}
private T _value;
private const int RETRY_COUNT_UNTIL_THROW = 10;
private static readonly string ERROR_EXCEEDED_RETRY_COUNT =
"Ref retried to Update for " + RETRY_COUNT_UNTIL_THROW + " times But there is always someone else intervened.";
}