在 Go 中的结构中使用互斥锁

Using a mutex within a struct in Go

我在Essential Go that using a mutex within a struct is not too straight-forward. To quote from the Mutex Gotchas页看到:

Don’t copy mutexes

A copy of sync.Mutex variable starts with the same state as original mutex but it is not the same mutex.

It’s almost always a mistake to copy a sync.Mutex e.g. by passing it to another function or embedding it in a struct and making a copy of that struct.

If you want to share a mutex variable, pass it as a pointer *sync.Mutex.

我不太确定我完全理解所写的内容。我看了 但还是不太清楚。

Set 的 Essential Go 示例为例,我是否应该像这样使用互斥锁:

type StringSet struct {
    m map[string]struct{}
    mu            sync.RWMutex
}

还是这样?

type StringSet struct {
    m map[string]struct{}
    mu            *sync.RWMutex
}

我在示例中尝试了 Delete() 函数,它们都在 Playground.

中工作
// Delete removes a string from the set
func (s *StringSet) Delete(str string) {
    s.mu.Lock()
    defer s.mu.Unlock()
    delete(s.m, str)
}

显然会有多个 'Set' 实例,因此每个实例都应该有自己的互斥量。在这种情况下,使用互斥锁还是指向互斥锁的指针更可取?

如果你走第一种方式,即

type StringSet struct {
    m map[string]struct{}
    mu            sync.RWMutex
}

结构值的任何意外或有意 assignment/copy 都会创建一个新的互斥锁,但底层映射 m 将是相同的(因为 maps are essentially pointers)。因此,可以在不锁定的情况下并发 modify/access 映射。当然,如果你严格遵守“你不得复制一套”的规则,那不会发生,但对我来说意义不大。

TL;DR: 绝对是第二种方式。

使用第一种方法(一个普通的 Mutex,而不是一个指向互斥锁的指针),并传递一个 *StringSet(指向你的结构的指针),而不是一个普通的 StringSet.

在您在 playground 中分享的代码中 (that version) :

  • .Add().Exists().Strings() 应该获取锁,
  • 否则你的代码适合在 go 中经常使用结构和互斥量。

如果您操纵纯 StringSet 结构,“不要复制互斥体”陷阱将适用:

var setA StringSet
setA.Add("foo")
setA.Add("bar")

func buggyFunction(s StringSet) {
  ...
}


// the gotcha would occur here :
var setB = setA
// or here :
buggyFunction(setA)

在上述两种情况下:您将创建完整结构的副本

例如,

so setB 将操纵与 setA 相同的底层 map[string]struct{} 映射,但不会共享互斥量:调用 setA.m.Lock() 不会防止从 setB.

修改映射