V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
92pretty

下面代码中 f()会被重复执行吗?大佬帮忙解答一下~

  •  
  •   92pretty · Jan 7 · 3214 views
    This topic created in 110 days ago, the information mentioned may be changed or developed.

    下面代码中 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()
    }
    
    
    
    10 replies    2026-04-17 15:32:49 +08:00
    ccpp132
        1
    ccpp132  
       Jan 7
    不会
    YanSeven
        2
    YanSeven  
       Jan 7
    额,你这个 demo 不都已经写好了,go run 一下立马知道。
    unused
        3
    unused  
       Jan 7 via Android
    不会,但是不能保证 Do() 返回时 f() 已经执行
    supuwoerc
        4
    supuwoerc  
       Jan 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()
    }
    }
    ```
    bv
        5
    bv  
       Jan 8
    不会,但 if o.done == 1 { return } 存在 data race 。
    92pretty
        6
    92pretty  
    OP
       Jan 8
    @supuwoerc 嗯嗯 弄清楚了,是这个理。happens before 的问题。f()可能会初始化一些配置、链接啥等,但是还没执行完,done 已经被设置为 1 了,后续进入的请求会直接 return , 认为 f()已经执行完成
    supuwoerc
        7
    supuwoerc  
       Jan 8
    @supuwoerc 是的,需要补充下,atomic.StoreUint32(&o.done, 1)应该 defer 执行,避免 happens before
    eudore
        8
    eudore  
       Jan 13
    不如直接用 atomic.CompareAndSwapInt32 ,原子比较设置。
    zzhirong
        9
    zzhirong  
       Jan 14
    @eudore 专门去看了下源码, 里面的注释还专门说了不能使用 CompareAndSwap, 原因就是, once.Do() 如果返回, 一定要确保 f() 已经被执行, 意味着运行 f() 的那个 goroutine 没返回的话, 其他调用 Do() 的 goroutine 也需要等待.

    源码: https://github.com/golang/go/blob/e2429619605951b137e25f6a51fbc39d9f0f1e9b/src/sync/once.go#L53
    ca2oh4
        10
    ca2oh4  
       10 days ago
    使用 `go run -race .\main.go` 运行 可以查看 数据竞争
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3673 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 45ms · UTC 04:52 · PVG 12:52 · LAX 21:52 · JFK 00:52
    ♥ Do have faith in what you're doing.