我想知道这是否是线程安全的,或者(如果不是)我如何使它安全。它是从计时器调用的:
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
。然后另一个线程进入主体,因为它尚未被线程 1 设置为 true
。即使 _isSynchronizing
是 volatile
,这也可能吗?如果是这样,使此方法线程安全的最佳方法是什么?如果我理解正确
volatile
不能防止这种竞争条件,但只能确保变量没有被缓存,所以所有线程总是读取当前值。
最佳答案
为保证线程安全,您必须将比较和分配到单个原子操作。否则,当第一个线程正在执行分配时,另一个线程总是有可能通过比较。 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
重载。
关于c# - 当我在两个连续的语句中读/写它时,我需要锁定一个 bool 值吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40654546/