
下面代码中 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 |
10 ca2oh4 6 天前 使用 `go run -race .\main.go` 运行 可以查看 数据竞争 |