sync.Mutex 在访问同一结构的不同字段时是否需要,但在单独的 goroutine 中?
Is sync.Mutex needed when accessing different fields of the same struct, but in separate goroutines?
我有一个结构,其中有几个简单的字段(即:int、string 和 []byte)。
我还有几个 goroutines 修改结构中的不同字段。但是每个goroutine修改自己的字段。
我没有注意到竞争条件的任何问题或提示。然后我很容易地将 sync.Mutex 添加到代码中,并且代码 运行 完全相同。
但根据我的阅读,似乎有些人可能建议在这种情况下使用 sync.Mutex。是否需要,即使程序运行没有错误?
出于锁定的目的,将结构的每个字段视为独立的。给定:
type Foo struct {
stringMap map[string]string
strings []string
numbers []float64
}
以下是安全的:
foo := &Foo{}
/* ... */
go func() {
foo.stringMap["foo"] = "bar"
}()
go func() {
foo.strings[0] = "baz"
}()
go func() {
fmt.Println(foo.numbers[0])
}()
但这是不安全的:
go func() {
fmt.Println(foo.numbers[0])
}()
go func() {
foo.numbers = append(foo.numbers, 123.456)
}()
因此您通常希望每个字段或每组可以一起修改的字段有一个互斥量:
type Foo struct {
stringMapMu sync.Mutex
stringMap map[string]string
stringsMu sync.Mutex
strings []string
numbersMu sync.Mutex
numbers []float64
}
然后:
go func() {
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
fmt.Println(foo.numbers[0])
}()
go func() {
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
foo.numbers = append(foo.numbers, 123.456)
}()
Flimzys 方法您必须非常小心!如果你想在同一个 goroutine 中处理结构的不同字段并且不跟踪字段的阻塞顺序,你可能会陷入僵局。 https://play.golang.org/p/wbgs40UoP-h
相同的阻塞顺序可以正常工作
//the same order stringMapMu-->numbersMu
go func() {
defer wg.Done()
foo.stringMapMu.Lock()//lock on stringMap, waits unlocking numbersMu by other goroutine
defer foo.stringMapMu.Unlock()
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
fmt.Println(foo.numbers, foo.stringMap)
}()
//works fine
//the same order stringMapMu-->numbersMu
go func() {
defer wg.Done()
foo.stringMapMu.Lock()//lock on stringMap, waits unlocking numbersMu by other goroutine
defer foo.stringMapMu.Unlock()
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
foo.numbers = append(foo.numbers, 123.456)
foo.stringMap["foo"] = "bar"
}()
但是相反的顺序是...死锁
//reverse order numbersMu-->stringMapMu
go func() {
foo.numbersMu.Lock()//lock on numbersMu, waits unlocking stringMapMu by other goroutine
defer foo.numbersMu.Unlock()
foo.stringMapMu.Lock()
defer foo.stringMapMu.Unlock()
foo.numbers = append(foo.numbers, 123.456)
foo.stringMap["foo"]="bar"
}()
此外,各个字段的锁可以隐含地隐藏在“Set”和“Get”方法中。你将在你的 goroutine 中以不同的顺序操作它们,你最终会遇到死锁 =(如果这不是性能问题,我更希望你的结构有一个 Mutex。
我有一个结构,其中有几个简单的字段(即:int、string 和 []byte)。
我还有几个 goroutines 修改结构中的不同字段。但是每个goroutine修改自己的字段。
我没有注意到竞争条件的任何问题或提示。然后我很容易地将 sync.Mutex 添加到代码中,并且代码 运行 完全相同。
但根据我的阅读,似乎有些人可能建议在这种情况下使用 sync.Mutex。是否需要,即使程序运行没有错误?
出于锁定的目的,将结构的每个字段视为独立的。给定:
type Foo struct {
stringMap map[string]string
strings []string
numbers []float64
}
以下是安全的:
foo := &Foo{}
/* ... */
go func() {
foo.stringMap["foo"] = "bar"
}()
go func() {
foo.strings[0] = "baz"
}()
go func() {
fmt.Println(foo.numbers[0])
}()
但这是不安全的:
go func() {
fmt.Println(foo.numbers[0])
}()
go func() {
foo.numbers = append(foo.numbers, 123.456)
}()
因此您通常希望每个字段或每组可以一起修改的字段有一个互斥量:
type Foo struct {
stringMapMu sync.Mutex
stringMap map[string]string
stringsMu sync.Mutex
strings []string
numbersMu sync.Mutex
numbers []float64
}
然后:
go func() {
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
fmt.Println(foo.numbers[0])
}()
go func() {
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
foo.numbers = append(foo.numbers, 123.456)
}()
Flimzys 方法您必须非常小心!如果你想在同一个 goroutine 中处理结构的不同字段并且不跟踪字段的阻塞顺序,你可能会陷入僵局。 https://play.golang.org/p/wbgs40UoP-h
相同的阻塞顺序可以正常工作
//the same order stringMapMu-->numbersMu
go func() {
defer wg.Done()
foo.stringMapMu.Lock()//lock on stringMap, waits unlocking numbersMu by other goroutine
defer foo.stringMapMu.Unlock()
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
fmt.Println(foo.numbers, foo.stringMap)
}()
//works fine
//the same order stringMapMu-->numbersMu
go func() {
defer wg.Done()
foo.stringMapMu.Lock()//lock on stringMap, waits unlocking numbersMu by other goroutine
defer foo.stringMapMu.Unlock()
foo.numbersMu.Lock()
defer foo.numbersMu.Unlock()
foo.numbers = append(foo.numbers, 123.456)
foo.stringMap["foo"] = "bar"
}()
但是相反的顺序是...死锁
//reverse order numbersMu-->stringMapMu
go func() {
foo.numbersMu.Lock()//lock on numbersMu, waits unlocking stringMapMu by other goroutine
defer foo.numbersMu.Unlock()
foo.stringMapMu.Lock()
defer foo.stringMapMu.Unlock()
foo.numbers = append(foo.numbers, 123.456)
foo.stringMap["foo"]="bar"
}()
此外,各个字段的锁可以隐含地隐藏在“Set”和“Get”方法中。你将在你的 goroutine 中以不同的顺序操作它们,你最终会遇到死锁 =(如果这不是性能问题,我更希望你的结构有一个 Mutex。