Mutex - 全局或本地和惯用用法?
Mutex - global or local and idiomatic usage?
阅读 golang.org 和 Whosebug 上的互斥示例后,我仍然不确定匿名函数的声明和惯用用法。所以我总结了几个例子。
示例 A、B 和 C 是否几乎相同,或者是否存在我没有注意到的重大差异?
我更喜欢全局示例“B”。我想如果我小心翼翼,这可能是最简单的解决方案。
或者是否有更好的方法来使用互斥量?
package main
import (
"fmt"
"sync"
)
type MuContainer struct {
sync.RWMutex
data int
}
var mucglobal = &MuContainer{}
func main() {
// A: Global declaration - working: adds 45
for i := 0; i < 10; i++ {
go func(j int, mucf *MuContainer) {
mucf.Lock()
mucf.data += j
mucf.Unlock()
}(i, mucglobal)
}
// B: Global only - working: adds 45
for i := 0; i < 10; i++ {
go func(j int) {
mucglobal.Lock()
mucglobal.data += j
mucglobal.Unlock()
}(i)
}
// C: Local declaration - working: adds 45
muclocal := &MuContainer{}
for i := 0; i < 10; i++ {
go func(j int, mucf *MuContainer) {
mucf.Lock()
mucf.data += j
mucf.Unlock()
}(i, muclocal)
}
// // D: Pointer to struct - not working: adds 0
// // I guess because it points directly to the struct.
// for i := 0; i < 10; i++ {
// go func(j int, mucf *MuContainer) {
// mucf.Lock()
// mucf.data += j
// mucf.Unlock()
// }(i, &MuContainer{})
// }
for {
mucglobal.RLock()
muclocal.RLock()
fmt.Printf("global: %d / local: %d\n", mucglobal.data, muclocal.data)
if mucglobal.data == 90 && muclocal.data == 45 {
muclocal.RUnlock()
mucglobal.RUnlock()
break
}
muclocal.RUnlock()
mucglobal.RUnlock()
}
}
D 不起作用,因为您正在为每次迭代创建一个新结构。最后,您将拥有 MuContainer
.
的 10 个独立实例
前两个选项在语义上是相同的。这两个的底线是每个 goroutine 共享对象的相同实例,它恰好是一个全局变量。
第二个类似,唯一的区别是锁定和更新的对象恰好是本地变量。同样,goroutines 在对象的同一个实例上工作。
所以这些其实并没有什么区别,三者各有用途。
阅读 golang.org 和 Whosebug 上的互斥示例后,我仍然不确定匿名函数的声明和惯用用法。所以我总结了几个例子。
示例 A、B 和 C 是否几乎相同,或者是否存在我没有注意到的重大差异? 我更喜欢全局示例“B”。我想如果我小心翼翼,这可能是最简单的解决方案。
或者是否有更好的方法来使用互斥量?
package main
import (
"fmt"
"sync"
)
type MuContainer struct {
sync.RWMutex
data int
}
var mucglobal = &MuContainer{}
func main() {
// A: Global declaration - working: adds 45
for i := 0; i < 10; i++ {
go func(j int, mucf *MuContainer) {
mucf.Lock()
mucf.data += j
mucf.Unlock()
}(i, mucglobal)
}
// B: Global only - working: adds 45
for i := 0; i < 10; i++ {
go func(j int) {
mucglobal.Lock()
mucglobal.data += j
mucglobal.Unlock()
}(i)
}
// C: Local declaration - working: adds 45
muclocal := &MuContainer{}
for i := 0; i < 10; i++ {
go func(j int, mucf *MuContainer) {
mucf.Lock()
mucf.data += j
mucf.Unlock()
}(i, muclocal)
}
// // D: Pointer to struct - not working: adds 0
// // I guess because it points directly to the struct.
// for i := 0; i < 10; i++ {
// go func(j int, mucf *MuContainer) {
// mucf.Lock()
// mucf.data += j
// mucf.Unlock()
// }(i, &MuContainer{})
// }
for {
mucglobal.RLock()
muclocal.RLock()
fmt.Printf("global: %d / local: %d\n", mucglobal.data, muclocal.data)
if mucglobal.data == 90 && muclocal.data == 45 {
muclocal.RUnlock()
mucglobal.RUnlock()
break
}
muclocal.RUnlock()
mucglobal.RUnlock()
}
}
D 不起作用,因为您正在为每次迭代创建一个新结构。最后,您将拥有 MuContainer
.
前两个选项在语义上是相同的。这两个的底线是每个 goroutine 共享对象的相同实例,它恰好是一个全局变量。
第二个类似,唯一的区别是锁定和更新的对象恰好是本地变量。同样,goroutines 在对象的同一个实例上工作。
所以这些其实并没有什么区别,三者各有用途。