在Go中对Locks / Mutex感到困惑

时间:2018-03-09 19:46:49

标签: go locking mutex

我正在尝试制作地图。通常所有读取都可以并行完成,除非写入时,所有读取都需要锁定。我以为我理解Mutex如何工作,但显然我没有。

我首先尝试使用RWMutex写锁:

type person struct {
    sync.RWMutex
    age int
}

func main() {
    a := person{age: 3}
    fmt.Println(a.age)
    go func() {
        a.Lock()
        time.Sleep(5 * time.Second)
        a.age = 4
        fmt.Println(a.age)
        a.Unlock()
    }()
    fmt.Println(a.age)
    fmt.Println("main", a.age)
    time.Sleep(20 * time.Second)
}

我有点期望写锁会锁定读操作a.age。相反,我得到了:

3
3
main 3
4

所以我决定添加一个读锁:

func main() {
    a := person{age: 3}
    fmt.Println(a.age)
    go func() {
        a.Lock()
        a.RLock()
        time.Sleep(5 * time.Second)
        a.age = 4
        fmt.Println(a.age)
        a.Unlock()
        a.RUnlock()
    }()
    fmt.Println(a.age)
    fmt.Println("main", a.age)
    time.Sleep(20 * time.Second)
}

更糟糕的是,我得到了:

3
3
main 3

显然我不明白这是如何运作的。谢谢你的帮助。

2 个答案:

答案 0 :(得分:1)

永远不要双锁。你的问题是你没有将main末尾的读取包装在锁中 - 如果他们没有尝试建立锁定,那么在其他东西写入时没有什么可以阻止他们阅读(即使写入使用锁定)。锁本身就是提供互斥(MutEx)的东西,因此只有在你持续使用它时才能得到它:

func main() {
    a := person{age: 3}
    fmt.Println(a.age)
    go func() {
        a.Lock()
        time.Sleep(5 * time.Second)
        a.age = 4
        fmt.Println(a.age)
        a.Unlock()
    }()
    a.RLock()
    fmt.Println(a.age)
    fmt.Println("main", a.age)
    a.RUnlock()
    time.Sleep(20 * time.Second)
}

这里没有神奇的事情发生;它实际上是对LockRLock进行锁定的调用。如果你不打电话给他们,没有什么能阻止并发访问。当你调用Lock时,它等待直到它可以锁定它自己,然后锁定它并返回。当你调用RLock时,它等待直到没有写锁,然后抓取(共享)读锁。它呼吁那些提供互斥的功能,而不是幕后发生的任何魔术。

答案 1 :(得分:0)

y=x; x = 2; z + x + y;

写锁定不会锁定变量,可能会导致竞争条件。 (https://blog.golang.org/race-detector

锁定只会同步对type person struct { sync.RWMutex age int } func main() { a := person{age: 3} fmt.Println(a.age) go func() { a.Lock() time.Sleep(5 * time.Second) a.age = 4 fmt.Println(a.age) a.Unlock() }() fmt.Println(a.age) fmt.Println("main", a.age) time.Sleep(20 * time.Second) } 3 <- 2nd line of `main` fmt.Println(a.age) 3 <- after go routine fmt.Println(a.age) main 3 <- fmt.Println("main", a.age) 4 <- goroutine after sleep fmt.Println(a.age) 的访问,使一次goroutine的写访问权限独占。它不会同步你的例程,这需要某种额外的同步。同步这两者的最常见模式之一是使用等待组:

https://golang.org/pkg/sync/#WaitGroup

a.age

等待组同步两个goroutines,确保最后一次打印为4