如果我Monitor.Enter有条件地在另一个线程处于临界区而没有锁定时会发生什么?

时间:2014-07-09 18:16:30

标签: c# .net multithreading

我正在尝试从系统类(Lazy<T>)重新实现功能,我发现了这个不寻常的代码。我明白了。尝试获取值的第一个线程执行计算。在发生这种情况时尝试的任何线程都会被锁定在门口,等待释放,然后去获取缓存的值。任何以后的调用都会注意到sentinel值,并且不再需要使用锁。

bool lockWasTaken = false;
var obj = Volatile.Read<object>(ref this._locker);
object returnValue = null;

try
{
    if (obj != SENTINEL_VALUE)
    {
        Monitor.Enter(obj, ref lockWasTaken);
    }

    if (this.cachedValue != null) // always true after code has run once
    {
        returnValue = this.cachedValue;
    }
    else //only happens on the first thread to lock and enter
    {
        returnValue = SomeCalculations();
        this.cachedValue = returnValue;
        Volatile.Write<object>(ref this._locker, SENTINEL_VALUE);
    }
    return returnValue
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(obj); 
    }
}

但是,让我们说,在代码更改后,另一种方法将this._locker重置为其原始值,然后进入锁定并重新计算缓存的值。虽然它这样做,但另一个线程碰巧正在拾取缓存的值,因此它位于锁定部分内,但没有锁定。怎么了?当带锁的线程同时并行时,它是否只是正常执行?

1 个答案:

答案 0 :(得分:3)

  

虽然它这样做,但另一个线程恰好是拿起缓存的值,所以它在锁定的部分内,但是没有锁定。怎么了?当带锁的线程同时并行时,它是否只是正常执行?

是的,它只是正常执行。

话虽如此,这段代码似乎可以使用Lazy<T>完全删除。 Lazy<T>类提供了一种线程安全的方法来处理数据的延迟实例化,这似乎是此代码的目标。

基本上,整个代码可以替换为:

// Have a field like the following:
Lazy<object> cachedValue = new Lazy<object>(() => SomeCalculations());

// Code then becomes:
return cachedValue.Value;