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

2 concurrency struct mutex go

我在Essential Go中看到,在结构体中使用互斥体并不是太简单。引用Mutex 陷阱页面:

\n
\n

Don\xe2\x80\x99t 复制互斥体

\n

变量的副本sync.Mutex以与原始互斥体相同的状态开始,但它不是相同的互斥体。

\n

sync.Mutex通过将eg传递给另一个函数或将其嵌入到结构中并制作该结构的副本来复制eg几乎总是错误的。

\n

如果您想共享互斥变量,请将其作为指针传递\n *sync.Mutex

\n
\n

我不太确定我是否完全理解所写的内容。我看了这里,但仍然不太清楚。

\n

以 Essential Go 的Set为例,我应该像这样使用互斥体:

\n
type StringSet struct {\n    m map[string]struct{}\n    mu            sync.RWMutex\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者像这样?

\n
type StringSet struct {\n    m map[string]struct{}\n    mu            *sync.RWMutex\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我在示例中尝试了两者的 Delete() 函数,它们都在Playground中工作。

\n
// Delete removes a string from the set\nfunc (s *StringSet) Delete(str string) {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    delete(s.m, str)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

LeG*_*GEC 5

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

在您在游乐场(该版本)中共享的代码中:

  • .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)
Run Code Online (Sandbox Code Playgroud)

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

setB例如,so将操作与 相同的底层map[string]struct{}映射setA,但互斥锁不会被共享:调用setA.m.Lock()不会阻止修改来自 的映射setB