当我在两个连续的陈述中读/写时,我是否需要锁定一个bool?

时间:2016-11-17 12:10:53

标签: c# .net multithreading locking

我想知道这是否是线程安全的,或者(如果没有)我是如何使它安全的。它从计时器调用:

private volatile bool _isSynchronizing;

private void SynchronizeSessionCache(object state = null)
{
    if (_isSynchronizing)
    {
        Log.Warn($"Aborted synchronization of SessionCache with SessionManager because we are already synchronizing. Interval is: {SynchronizationInterval}");
        return;
    }

     _isSynchronizing = true;

    bool lockWasTaken = false;
    try
    {
        // some code  that doesn't need a lock ...
        // ...

        // lock this part
        Monitor.Enter(_lockObject, ref lockWasTaken);
        // main code ...
    } 
    finally  // omitted catch
    {
        _isSynchronizing = false; 
        if(lockWasTaken)
            Monitor.Exit(_lockObject);
    }
}

我担心的是,一个线程可能会在他的方法开头检查_isSynchronizing,此时它是false。然后另一个线程进入正文,因为它还没有被thread1设置为true。即使_isSynchronizingvolatile,这可能吗?如果是这样,那么使这个方法成为线程安全的最佳方法是什么?

如果我理解正确volatile无法阻止此类竞争条件,但只能确保变量未被缓存,因此所有线程始终读取当前值。

1 个答案:

答案 0 :(得分:4)

为确保线程安全,您必须对单个原子操作进行比较和赋值。否则,在第一个线程执行赋值时,另一个线程总是有可能通过比较。 volatile关键字在这里没有帮助,因为它告诉编译器(和运行时)不允许进行优化,这可能会改变此变量所涉及的读写操作的顺序。

(有关volatile内容的更多信息,请参阅Eric Lippert撰写的this精彩文章。

简单(稍微慢一点)的解决方案是围绕比较和分配建立一个关键部分:

lock (_someLockObject)
{
    if (_isSynchronizing)
    {
         return;
    }

   _isSynchronizing = true;    
}

但是有一个更快的解决方案,但不适用于bool变量。对于int,您可以使用Interlocked.CompareExchange()方法。

让我们假装int _isSynchronizing = 0表示false_isSynchronizing = 1表示true

然后你可以使用这个声明:

if (Interlocked.CompareExchange(ref _isSynchronizing, 1, 0) == 1)
{
    // If the original val == 1, we're already synchronizing
    return;
}

这比使用Monitor略快,但没有bool重载。