【问题标题】:Understanding mutex behviour了解互斥体行为
【发布时间】:2020-12-05 14:51:03
【问题描述】:

我在想 Go 中的 mutex 会锁定数据,并且不允许任何其他 goroutine 读/写,除非拳头 goroutine 释放锁定。看来我的理解是错误的。阻止其他goroutine 读/写的唯一方法是在其他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 无法读取/写入结构,但这里不会发生这种情况。

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

我觉得mutex 在 Go 中的工作方式有点奇怪。如果我锁定,那么 Go 不应该允许任何其他 goroutine 读取/写入它。

有人可以向我解释 Go 中的互斥锁概念吗?

【问题讨论】:

  • 你为什么说第一个 goroutine “锁定了结构”呢?它锁定了一个互斥锁,是的。是什么让您认为互斥锁锁定了结构而不是其他东西?
  • 我在想结构锁中的互斥锁不仅仅是互斥锁,还有结构本身,这只是我的理解不好。

标签: multithreading go mutex goroutine


【解决方案1】:

互斥体周围没有神奇的力场,可以保护它碰巧嵌入的任何数据结构。如果您锁定了一个互斥体,它会阻止其他代码锁定它,直到它被解锁。不多也不少。 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
}

【讨论】:

  • 知道了,但我个人觉得它很混乱。无论如何,如果代码中的某个地方缺少锁定/解锁,它只会毁掉其他一切。
  • 是的,同步和并发需要非常小心,否则会出现死锁或发生奇怪的事情。竞赛检查器可以提供帮助,对可变状态的良好设计和封装也可以提供帮助,但一开始并不容易。
  • “如果代码中的某个地方缺少锁定/解锁,它只会毁掉其他一切”——所有类型的代码都可以这样说。如果您缺少任何代码,它可能会破坏一切。互斥体在这方面并不特殊。
  • @Mohammed True。这正是原因,只要人为可能,您应该not communicate by sharing memory, but share memory by communicating
猜你喜欢
  • 2015-09-26
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 2012-12-13
  • 1970-01-01
  • 2013-06-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多