我想知道这是否是线程安全的,或者(如果没有的话)我如何使其安全.它从计时器调用:
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过载.