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

Can I use lock to ensure instruction order?

如本 link 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 受 Lock 保护:

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()
}

因为 link 说:

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.

但是不清楚a和b的赋值是否保证在Unlock语句

之前执行

保证一旦f执行mu.Unlock(),所有读取ab的goroutines都会看到a和[=的更新值13=] 如果这些 goroutines 也使用相同的锁访问 ab。如果有 goroutines 在没有锁定的情况下读取 ab 那么就会有一场比赛。

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

虽然没有定义行为。关于goroutine调度没有同步,所以可能是g()先锁住mutex,一旦释放,main()结束,你的app可能会随之结束,而f()可能不会完成(甚至可能不会开始)。该应用程序可能不打印任何内容,可能会打印 2,或者可能会打印 21.

也可能是f()先加锁,进行赋值,然后g() in 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 来解决。这不是它的设计用途,但这也有效:

main() 中锁定 mu,然后再启动 goroutine。有 g() 也加锁,一开始显然会阻塞。 f() 可以在它的工作完成后解锁它,将 "green light" 给 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 上试试这个。