我想知道这是否是线程安全的,或者(如果没有)我是如何使它安全的。它从计时器调用:
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
。即使_isSynchronizing
是volatile
,这可能吗?如果是这样,那么使这个方法成为线程安全的最佳方法是什么?
如果我理解正确volatile
无法阻止此类竞争条件,但只能确保变量未被缓存,因此所有线程始终读取当前值。
答案 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
重载。