了解互斥行为

Understanding mutex behviour

我在想 Go 中的 mutex 会锁定数据并且不允许任何其他 goroutine read/write 除非拳头 goroutine 释放锁。看来我的理解是错误的。从其他 goroutine 中阻止 read/write 的唯一方法是在其他 goroutines 中也调用 lock。这将确保 critical section 被一个且只有一个 goroutine.

访问

所以,我预计 this 代码会出现死锁:

package main

import(
    "fmt"
    "sync"
)

type myMap struct {
    m map[string]string
    mutex sync.Mutex
}

func main() {
    done := make(chan bool)
    ch := make(chan bool)
    myM := &myMap{
        m:     make(map[string]string),
    }
    go func() {
        myM.mutex.Lock()
        myM.m["x"] = "i"
        fmt.Println("Locked. Won't release the Lock")
        ch <- true
    }()

    go func() {
        <- ch
        fmt.Println("Trying to write to the myMap")
        myM.m["a"] = "b"
        fmt.Println(myM)
        done <- true
    }()
    <- done
}

由于第一个 goroutine 锁定结构,我希望第二个 goroutine 无法 read/write 到结构,但这里没有发生。

如果我在第二个 goroutine 中添加 mux.Lock() 那么就会出现死锁。

我发现 mutex 在 Go 中的工作方式有点奇怪。如果我锁定,那么 Go 不应该允许任何其他 goroutine 到 read/write 到它。

谁能给我解释一下 Go 中的互斥量概念?

互斥体周围没有神奇的力场来保护它恰好嵌入其中的任何数据结构。如果锁定互斥体,它会阻止其他代码锁定它,直到它被解锁。仅此而已。 It's well documented in the sync package.

所以在您的代码中,只有一个 myM.mutex.Lock(),效果与没有互斥锁一样。

正确使用互斥锁来保护数据涉及在更新或读取数据之前锁定互斥锁,然后再将其解锁。通常这段代码会被包裹在一个函数中,这样就可以使用 defer 了:

func doSomething(myM *myMap) {
    myM.mutex.Lock()
    defer myM.mutex.Unlock()
    ... read or update myM
}