英文:
Use mutex or mutex pointer in Golang?
问题
我有一个代码片段:
type calculation struct{
sum int
mutex sync.Mutex
}
func dosomething(c *calculation , wg *sync.WaitGroup) {
c.mutex.Lock()
c.sum++
c.mutex.Unlock()
wg.Done()
}
func main() {
t := time.Now()
c := new(calculation)
wg := new(sync.WaitGroup)
for i:=0; i<10000000; i++{
wg.Add(1)
go dosomething(c, wg)
}
wg.Wait()
fmt.Println(c.sum)
fmt.Println(time.Since(t))
}
但我发现使用互斥锁指针也可以:
type calculation struct{
sum int
mutex *sync.Mutex
}
func dosomething(c *calculation , wg *sync.WaitGroup) {
c.mutex.Lock()
c.sum++
c.mutex.Unlock()
wg.Done()
}
func main() {
t := time.Now()
c := &calculation{0, new(sync.Mutex)}
wg := new(sync.WaitGroup)
for i:=0; i<10000000; i++{
wg.Add(1)
go dosomething(c, wg)
}
wg.Wait()
fmt.Println(c.sum)
fmt.Println(time.Since(t))
}
我对这两个版本进行了一些测试,发现它们的执行时间接近。
那么我应该使用哪个版本?为什么要使用互斥锁或互斥锁指针?它们之间有性能差异吗?
英文:
I have a code snippet:
type calculation struct{
sum int
mutex sync.Mutex
}
func dosomething(c *calculation , wg *sync.WaitGroup) {
c.mutex.Lock()
c.sum++
c.mutex.Unlock()
wg.Done()
}
func main() {
t := time.Now()
c := new(calculation)
wg := new(sync.WaitGroup)
for i:=0; i<10000000; i++{
wg.Add(1)
go dosomething(c, wg)
}
wg.Wait()
fmt.Println(c.sum)
fmt.Println(time.Since(t))
}
But I found that use a mutex pointer works too:
type calculation struct{
sum int
mutex *sync.Mutex
}
func dosomething(c *calculation , wg *sync.WaitGroup) {
c.mutex.Lock()
c.sum++
c.mutex.Unlock()
wg.Done()
}
func main() {
t := time.Now()
c := &calculation{0, new(sync.Mutex)}
wg := new(sync.WaitGroup)
for i:=0; i<10000000; i++{
wg.Add(1)
go dosomething(c, wg)
}
wg.Wait()
fmt.Println(c.sum)
fmt.Println(time.Since(t))
}
I made some tests on these 2 versions and found that there elapsed times are close.
So which one should I use? And Why should I use a mutex or mutex pointer? Are they have performance differences?
答案1
得分: 6
一个Go的sync.Mutex
无法被复制,所以当你有一个数据结构可以被合理地复制,但其中一部分是共享的且不能被合理地复制时,你会使用指向(共享的、非复制的)sync.Mutex
的指针,并且你计划通过值传递或返回它,以便它被复制。
例如:
type Thingy struct {
notShared int
mtx *Sync.mutex
shared *int
}
func (t Thingy) Thingy {
t.notShared += 3
t.mtx.Lock()
*t.shared *= 2
t.mtx.Unlock()
return t
}
(更典型的情况可能是有多个非共享字段,然后一个指向包含互斥锁本身和共享字段的结构体的指针。上面的代码主要是为了说明)
考虑到你的calculation
结构体总是作为指针传递的,所以在你的示例中没有理由增加一层间接性。
编辑:你提出了一个关于性能差异的问题。添加一个额外的(否则不必要的)间接性通常会在性能上付出一些代价,但一般来说,你应该首先编写最可读的代码,然后再测量性能问题。在我看来,添加一个额外的不必要的间接性往往会使代码稍微难以理解,所以在这里,性能和可读性是相辅相成的。
英文:
A Go sync.Mutex
cannot be copied, so you would use a pointer to a (shared, non-copied) sync.Mutex
when you have a data structure that can be copied sensibly but that has some portion of itself that is shared and cannot be copied sensibly, and you plan to pass it around by value and/or return it, so that it gets copied.
For instance:
type Thingy struct {
notShared int
mtx *Sync.mutex
shared *int
}
func (t Thingy) Thingy {
t.notShared += 3
t.mtx.Lock()
*t.shared *= 2
t.mtx.Unlock()
return t
}
(It would probably be more typical to have multiple non-shared fields, and then one pointer to a struct
that contains both the mutex itself and the shared fields. The above is mainly for illustration)
Given that your calculation
structure is itself always passed around as a pointer, though, there's no reason to add a level of indirection to your example.
Edit: you added a question about performance differences. Adding an extra (otherwise-unnecessary) indirection often costs a bit in performance, but in general, you should just write the most readable code you can first, and then measure it for performance issues. Adding an extra unnecessary indirection tends to make the code slightly less readable, in my view, so here, performance and readability go together.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论