我正在Ref<T>
使用无锁乐观更新方法进行小型抽象(应该使用不可变T
数据结构,特别是不可变树)。它基于SpinWait 使用LockFreeUpdate
部分中 Albahari 的“C#中的线程”方法:
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 的完整 Ref 代码(检查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.";
}