我可以使用锁来确保指令顺序吗?

问题描述 投票:0回答:2

如此链接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的赋值

go concurrency mutex memory-model
2个回答
3
投票

保证f执行mu.Unlock()后,所有读取ab的goroutine将看到ab的更新值,前提是这些goroutine也访问了ab使用相同的锁。如果有goroutines读取了ab而没有锁定,则说明存在种族。


4
投票

该代码是安全的,因为它没有数据争用。

虽然行为未定义。关于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上尝试此。

© www.soinside.com 2019 - 2024. All rights reserved.