如此链接https://golang.org/ref/mem中所述,以下代码使用了不正确的同步:
var a, b int
func f() {
a = 1
b = 2
}
func g() {
print(b)
print(a)
}
func main() {
go f()
g()
}
因为有可能打印a = 0和b = 2。
但是,我想知道下面的代码中用锁保护a和b的结果是否可行:
var a, b int
var mu sync.Mutex
func f() {
mu.Lock()
a = 1
b = 2
mu.Unlock()
}
func g() {
mu.Lock()
print(b)
print(a)
mu.Unlock()
}
func main() {
go f()
g()
}
因为链接说:
For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() happens before call m of l.Lock() returns.
但是,尚不清楚是否保证在Unlock
语句之前执行a和b的赋值
保证f
执行mu.Unlock()
后,所有读取a
和b
的goroutine将看到a
和b
的更新值,前提是这些goroutine也访问了a
和b
使用相同的锁。如果有goroutines读取了a
和b
而没有锁定,则说明存在种族。
该代码是安全的,因为它没有数据争用。
虽然行为未定义。关于goroutine调度没有同步,因此可能是g()
首先锁定了互斥锁,一旦它释放,main()
就会结束,您的应用可能会随之终止,并且f()
可能无法完成(甚至可能无法启动) )。该应用程序可能什么也不打印,可能打印2
或可能打印21
。
也可能是f()
首先锁定,将分配移出,然后g()
中的main()
将打印分配的新值:21
。
如果要先分配f()
,则可以使用sync.WaitGroup
,例如:
var a, b int
var wg sync.WaitGroup
func f() {
defer wg.Done()
a = 1
b = 2
}
func g() {
print(b)
print(a)
}
func main() {
wg.Add(1)
go f()
wg.Wait()
g()
}
这将始终打印21
,请在Go Playground上尝试。
还有一个复杂的例子证明也可以用sync.Mutex
解决。这不是其设计用途,但也可以使用:
启动Goroutine之前,将mu
中的main()
锁定。也将g()
锁定,这显然会首先阻塞。 f()
可以在完成其工作后将其解锁,为g()
赋予“绿灯”:
var a, b int
var mu sync.Mutex
func f() {
a = 1
b = 2
mu.Unlock()
}
func g() {
mu.Lock()
print(b)
print(a)
mu.Unlock()
}
func main() {
mu.Lock()
go f()
g()
}
在Go Playground上尝试此。