这是另一个
don't be smart
GO问题。当然有 once
原语可以使用,但这纯粹是为了讨论目的。
type impl struct {
fLock *sync.Mutex
fValue TValue
fGetValueFunc XXXX
}
var err basic.Error = nil
lock := impl.fLock
if lock != nil { // RACE
lock.Lock()
defer lock.Unlock()
if impl.fLock != nil {
impl.fValue, err = impl.fGetValueFunc()
if err == nil {
impl.fGetValueFunc = nil
impl.fLock = nil // RACE CHECK Complains
}
}
}
竞赛检查抱怨并发访问。但我假设这只是一个地址,并且已经保证对机器字的访问保证是原子的。
尽管竞赛检查抱怨,这种双重检查锁定仍然有效吗?
通过竞争检测器,编译器使用记录访问内存的时间和方式的代码来检测所有内存访问,而运行时库则监视对共享变量的不同步访问。
因此,竞争检测器正在寻找对不受某些同步保护的共享变量(即互斥体、通道等)的访问。
lock := impl.fLock // create a new var with the same pointer value
if lock != nil { // READ the value of that pointer (outside synchronization)
lock.Lock()
defer lock.Unlock()
// trimmed
impl.fLock = nil // Write the value of that pointer (inside synchronization)
竞争检测器不知道代码的含义。但您可以合理地假设所有种族警告都应该得到修复(您不想养成忽略它们的习惯)。
在没有看到更广泛的代码上下文的情况下,很难确定程序的行为是什么。例如;
lock
有没有改变,并发在哪里等等
我们所知道的是:
lock
和 impl.fLock
开始时是指向某个内存地址的同一个互斥体的两个指针,例如: 0xc0000ac040
lock
从未被重新分配,则互斥量被锁定在 lock.Lock()
impl.fLock = nil
不会更改互斥锁,它只是将指针从 0xc0000ac040
更改为 0x0
(nil),lock
的值将保持为 0xc0000ac040
我们不知道
impl.fLock
被分配到哪里(或者更糟糕的是,重新分配)
impl.fLock
,那么我们可能有两个独立的互斥体,一个位于原始地址0xc0000ac040
,另一个位于某个新地址; 0xc000104040
lock.Lock()
不再保护以下块: if impl.fLock != nil {
// it's possible that some concurrent impl.fGetValueFunc = nil
// gets scheduled between the nil check and the below line, so
// impl.fGetValueFunc would panic
impl.fValue, err = impl.fGetValueFunc()
if err == nil {
impl.fGetValueFunc = nil