c#-在两个连续的语句中读取/写入布尔时,是否需要锁定布尔?

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

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,但此时是错误的.然后,另一个线程进入主体,因为线程1尚未将其设置为true.即使_isSynchronizing是易失的,这是否可能?如果是这样,使该方法成为线程安全的最佳方法是什么?

如果我正确理解它,volatile不能阻止这种竞争情况,只能确保不缓存该变量,因此所有线程始终读取当前值.

解决方法:

为了确保线程安全,您必须对单个原子操作进行比较和分配.否则,在第一个线程正在执行分配时,总是有另一个线程可以通过比较的机会. volatile关键字在这里无济于事,因为它告诉编译器(和运行时)不允许进行任何优化,这可能会更改此变量所涉及的读写操作的顺序.

(有关您可以在Eric Lippert的this伟大文章中阅读的易失性内容的更多信息.)

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

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

   _isSynchronizing = true;    
}

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

假设_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过载.

上一篇:如何实现运行php脚本的单处理模式?


下一篇:超出MySQL锁等待超时