在 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
.
修改映射
我在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
.