【问题标题】:Can I use lock to ensure instruction order?我可以使用锁来确保指令顺序吗?
【发布时间】:2020-02-21 14:13:59
【问题描述】:

正如此链接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()
}

因为链接说:

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语句之前执行

【问题讨论】:

  • 第一个锁是解锁操作,是系统调用。您正在锁定打印操作。当线程进入系统调用(等待操作)时,它创建条件将上下文切换到之前创建的goroutine。它遇到第二个锁并等待直到第一个锁被解锁。因此,变量只有在打印后才会更改。
  • @LaevusDexter:这是不正确的,因为g 调用了系统调用,所以没有隐含的同步。无法保证调度程序会在f 获得锁定并更改值之前返回到main

标签: go concurrency mutex memory-model


【解决方案1】:

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

虽然没有定义行为。没有关于goroutine调度的同步,所以可能是g()先锁了互斥锁,一旦释放,main()结束,你的应用可能会以它结束,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 解决。这不是它的设计用途,但这也有效:

在启动 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 上试试这个。

【讨论】:

    【解决方案2】:

    保证一旦f执行mu.Unlock(),所有读取ab的goroutine将看到ab的更新值,前提是这些goroutine也访问a和@987654328 @ 使用相同的锁。如果有 goroutine 在没有锁的情况下读取 ab,那么就会出现竞争。

    【讨论】:

    • 您好,感谢您的回复。这是否意味着代码中Unlock语句之前的任何语句都保证在Unlock语句之前执行?
    • 是的,这是有保证的。对于单个 goroutine,它总是保证是这样的。对于多个 goroutine,锁定保证了这些指令的执行对其他 goroutine 可见。
    猜你喜欢
    • 1970-01-01
    • 2012-10-22
    • 2014-07-01
    • 1970-01-01
    • 2021-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    相关资源
    最近更新 更多