【问题标题】:Race condition with mutex in go and where to embed the lock, parent struct or child struct?go中带有互斥锁的竞争条件以及在哪里嵌入锁、父结构或子结构?
【发布时间】:2019-06-18 09:55:58
【问题描述】:
  1. 我看到有些人在一个结构中取出了一些互斥锁。有必要吗?或者我们可以简单地在那里放一个锁吗? 示例:
type Session struct {
    // some other irrelevant code
    pingLock sync.Mutex
    // some other irrelevant code
    streamLock sync.Mutex
    // some other irrelevant code
    shutdownLock sync.Mutex
}
  1. 如果一个结构包含另一个结构,而子结构包含映射或切片,我应该将互斥体、父结构或子结构放在哪里?
  2. 为什么 mutex.Lock() 在我的代码中不起作用?每次我进行比赛测试时,它都会显示有比赛。

起初我试图解决前两个问题,所以我写了一些演示代码来测试它。我尝试使用子结构中的锁,然后使用父结构来锁定状态,但它们都不起作用。比赛测试一直说有比赛条件。 我试过t.Lock()t.data.Lock()

type Test struct {
    name string
    data Data
    sync.RWMutex //Should I put it here?
}

type Data struct {
    d map[string]int
    sync.RWMutex // Should I put it here?
}

func (t *Test) add(key string) {
    t.data.Lock()
    defer t.data.Unlock()
    t.data.d[key] += 1
}

func (t *Test) read() {
    for {
        t.data.Lock()
        _= t.data.d["test"]
        t.data.Unlock()
    }
}

func main() {

    t := &Test{}
    t.name = "oops"
    t.data = Data{}
    t.data.d = make(map[string]int)
    t.data.d["test"] = 1

    for i := 0; i <= 10; i++ {
        go func(t *Test) {
            t.add("test")
        }(t)
        go func(t *Test) {
            t.read()
        }(t)
    }
    time.Sleep(time.Second * 3)
    fmt.Printf("result is %v", t.data.d["test"])

【问题讨论】:

    标签: go struct concurrency mutex race-condition


    【解决方案1】:

    正如@peterSO 提到的,错误是由“fmt.Printf("result is %v\n", t.data.d["test"])" 引起的。 经过几个小时的挖掘,我似乎找到了关于前两个问题的答案。我误解了互斥锁的概念。互斥锁用于保护资源,而不是锁定它自己的内存,在我的示例中,是结构本身。 对于第一个问题: 如果一个 goroutine 执行一些代码,比如

    s.pingLock.Lock()
    \\ some logic 1
    s.pingLock.Unlock()
    streamLock
    \\ some logic 2
    streamUnlock
    

    所以当一个goroutine执行这段代码并获取s.streamLock.Lock()时,直到它被解锁,其他goroutine不能执行some logic 2,但是任何goroutine如果得到some logic1就可以执行some logic1。如果只使用一个锁,如果一个 goroutine 获得了锁,那么其他人都无法获得锁,那么所有其他执行都会被阻塞。

    第二个问题: 如果理解上面的解释。然后也回答了第二个问题。把互斥锁放在你想要的任何地方,因为它只是一个保护资源的锁,也就是保护代码本身。不过,我确信有一种很好的惯用方式。

    我不确定这样处理是否正确。如果有人有不同的意见或更好的答案,请告诉我。

    【讨论】:

      【解决方案2】:

      竞态测试一直说有竞态条件。


      声明

      fmt.Printf("result is %v", t.data.d["test"])
      

      传递参数

      t.data.d["test"]
      

      按价值。它通过赋值进行复制,即读取。

      您需要通过互斥锁保护读取。

      t.data.Lock()
      fmt.Printf("result is %v\n", t.data.d["test"])
      t.data.Unlock()
      

      【讨论】:

      • 感谢您指出这一点。它确实解决了这个问题。我知道现在是阅读。但是为什么按值传递参数很重要呢?我应该把我的互斥锁放在哪里,在 Test struct 、 Data Struct 或两者中?
      • 我已经更新了我的问题。你能帮忙吗?
      猜你喜欢
      • 2014-11-08
      • 2012-10-27
      • 2022-01-15
      • 2018-10-12
      • 1970-01-01
      • 1970-01-01
      • 2012-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多