下面代码中 f()会被重复执行吗?
package main
import (
"fmt"
"sync"
)
type Once struct {
m sync.Mutex
done uint32
}
func (o *Once) Do(f func()) {
if o.done == 1 {
return
}
o.m.Lock()
defer o.m.Unlock()
fmt.Println("bing: ", o.done)
if o.done == 0 {
o.done = 1
f()
}
}
func main() {
var once Once
wg := sync.WaitGroup{}
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
once.Do(func() {
println("executed---------》 ")
})
}()
}
wg.Wait()
}
1
ccpp132 1 月 7 日
不会
|
2
YanSeven 1 月 7 日
额,你这个 demo 不都已经写好了,go run 一下立马知道。
|
3
unused 1 月 7 日 via Android
不会,但是不能保证 Do() 返回时 f() 已经执行
|
4
supuwoerc 1 月 8 日
存在 data race ,你想要双重检查标识位,但是第一次检查没在加锁之后,是存在潜在问题的。
问题出现在 done uint32 的读写并不是原子的,你需要换成原子读写,你这段代码和 sync.Once 的区别就是这里。 ``` func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 1 { return } o.m.Lock() defer o.m.Unlock() fmt.Println("bing: ", o.done) if atomic.LoadUint32(&o.done) == 0 { atomic.StoreUint32(&o.done, 1) f() } } ``` |
5
bv 1 月 8 日
不会,但 if o.done == 1 { return } 存在 data race 。
|
6
92pretty OP @supuwoerc 嗯嗯 弄清楚了,是这个理。happens before 的问题。f()可能会初始化一些配置、链接啥等,但是还没执行完,done 已经被设置为 1 了,后续进入的请求会直接 return , 认为 f()已经执行完成
|
8
eudore 1 月 13 日
不如直接用 atomic.CompareAndSwapInt32 ,原子比较设置。
|
9
zzhirong 1 月 14 日
@eudore 专门去看了下源码, 里面的注释还专门说了不能使用 CompareAndSwap, 原因就是, once.Do() 如果返回, 一定要确保 f() 已经被执行, 意味着运行 f() 的那个 goroutine 没返回的话, 其他调用 Do() 的 goroutine 也需要等待.
源码: https://github.com/golang/go/blob/e2429619605951b137e25f6a51fbc39d9f0f1e9b/src/sync/once.go#L53 |