• liangjf
    2019-02-26
    “双重检查” 貌似也并不是完全安全的吧,像c++11那样加入内存屏障才是真正线性安全的。go有这类接口吗

    作者回复: Go语言底层内置了内存屏障。它的好处就是不用像C++那样什么都需要自己搞。

    
     9
  • 唐大少在路上。。。
    2019-10-24
    个人感觉Once里面的逻辑设计得不够简洁,既然目的就是只要能够拿到once的锁的gorountine就会消费掉这个once,那其实直接在Do方法的最开始用if atomic.CompareAndSwapUint32(&o.done, 0,1)不就行了,连锁都不用。
    还请老师指正,哈哈

    作者回复: 这样不行啊,它还得执行你给它的函数啊。怎么能在没执行函数之前就把 done 变成 1 呢,对吧。但如果是在执行之后 swap,那又太晚了,有可能出现重复执行函数的情况。

    所以 Once 中才有两个执行路径,一个是仅包含原子操作的快路径,另一个是真正准备执行函数的慢路径。这样才可以兼顾多种情况,让总体性能更优。

    
     3
  • 超大叮当当
    2019-03-13
    sync.Once 不用 Mutex ,直接用 atomic.CompareAndSwapUint32 函数也可以安全吧?

    作者回复: 原子操作是CPU级别的互斥,而且防中断。但是支持的数据类型很少,而且并不灵活。所以如果是对代码块进行保护,还需要用锁。

    
     3
  • 蔺晨
    2018-11-21
    思考题 :
    func getAllGoroutineResult(){
            wg := sync.WaitGroup{}
            wg.Add(3)

            once := sync.Once{}
            var aAndb int
            var aStrAndb string
            var gflag int32

            addNum := func(a,b int, ret *int) {
                defer wg.Done()
                time.Sleep(time.Millisecond * 2000)
                *ret = a+b
                atomic.AddInt32(&gflag,1)
            }

            addStr := func(a,b string, ret *string) {
                defer wg.Done()
                time.Sleep(time.Millisecond * 1000)
                *ret = a+b
                atomic.AddInt32(&gflag,1)
            }

            // waitRet需要等待 addNum和addStr执行完成后的结果
            waitRet := func(ret *int, strRet *string) {
                defer wg.Done()
                once.Do(func() {
                    for atomic.LoadInt32(&gflag) != 2 {
                        fmt.Println("Wait: addNum & addStr")
                        time.Sleep(time.Millisecond * 200)
                    }
                })
                fmt.Println(fmt.Sprintf("AddNum's Ret is: %d\n", *ret))
                fmt.Println(fmt.Sprintf("AddStr's Ret is: %s\n", *strRet))
            }

            // waitRet goroutine等待AddNum和AddStr结束
            go waitRet(&aAndb, &aStrAndb)
            go addNum(10, 20, &aAndb)
            go addStr("测试结果", "满意不?", &aStrAndb)

            wg.Wait()
    }
    展开
    
     3
  • 虢國技醬
    2019-11-26
    二刷走起
    
     2
  • ricktian
    2018-11-30
    执行结果如果不用channel实现,还有什么方法?请老师指点~
     1
     2
  • Laughing
    2018-10-30
    子任务的结果应该用通道来传递吧。另外once的应用场景还是没有理解。郝大能简单说一下么?

    作者回复: 可以通过通道,但这就不是wg的作用范围了。once一般是执行只应该执行一次的任务,比如初始化连接池等等。你可以在go源码里搜一下,用的地方还是不少的。

    
     2
  • ONLY
    2019-12-10
    可不可以把 sync.once 理解为单例模式,比如连接数据库只需要连接一次,把连接数据库的代码实在once.do()里面

    作者回复: 它跟单例模式还不太一样。单例模式指的是某类结构的唯一实例,而 once 指的是对某段代码的唯一一次执行。它们的维度不一样。

    连接数据库的代码其实不太适合放到 Do 里面执行,或者说不太恰当。初始化数据库链接的代码可以放到里面。而,断链重连的机制也应该在其中。

    
     1
  • 手指饼干
    2019-10-10
    请问老师,如下deferFunc为什么要用func包装起来,直接使用defer deferFunc()不可以吗?
    func addNum(numP *int32, id, max int32, deferFunc func()) {
        defer func() {
            deferFunc()
        }()
        //...
    }
    展开

    作者回复: 你那么写也可以,我弄一坨只是想引起你们的注意。在不影响程序功能和运行效率的前提下,我会在程序里尽量多展示几种写法。

    
     1
  • undifined
    2018-10-22
    执行结果用 Callback,放在通道中,在主 goroutine 中接收返回结果
    
     1
  • Alan
    2020-01-10
    哈哈,”快速失败路径“ 这个称呼还可以,有点类似java的fail-fast 机制
    
    
  • 窗外
    2019-11-03
    go func () {
             wg.Done()
            fmt.Println("send complete")
         }()
    老师,为什么在Done()后的代码就不会被执行呢?

    作者回复: 你在后面 wg.Wait() 了吗?

     1
    
  • nora
    2019-10-15
    package main

    import (
        "fmt"
        "sync"
    )

    //不要把增加其计数器值的操作和调用其Wait方法的代码,放在不同的 goroutine 中执行
    //要杜绝对同一个WaitGroup值的两种操作的并发执行

    //使用WaitGroup值标准方式: 先统一Add,再并发Done,最后Wait

    func main() {
        var wg sync.WaitGroup
        i := 0
        for ; i < 2; i++ {
            wg.Add(1)
            go func(i int) {
                fmt.Println("i = ", i)
                defer wg.Done()
            }(i)
        }
        wg.Wait()
        return
    }
    展开
    
    
  • bluuus
    2019-09-06
    func coordinateWithWaitGroup() {
        var wg sync.WaitGroup
        wg.Add(2)
        num := int32(0)
        fmt.Printf("The number: %d [with sync.WaitGroup]\n", num)
        max := int32(10)
        go addNum(&num, 3, max, wg.Done)
        //go addNum(&num, 4, max, wg.Done)
        wg.Wait()
    }

    run result:
    The number: 0 [with sync.WaitGroup]
    The number: 2 [3-0]
    The number: 4 [3-1]
    The number: 6 [3-2]
    The number: 8 [3-3]
    The number: 10 [3-4]
    fatal error: all goroutines are asleep - deadlock!

    执行这段代码会死锁,我以为最多在wai()方法那儿阻塞,谁能解释一下?
    展开

    作者回复: 你对 addNum 函数有改动吗?

     2
    
  • magina
    2019-08-05
    WaitGroup能不能设置超时

    作者回复: 本身不能,但是这很容易通过 select 语句和 time.Timer 解决。

    
    
  • 虢國技醬
    2019-07-26
    不用chan的情况下,
    传递一个指针变量到go执行的函数中,这样就可以在主goroutine中取到
    
    
  • M
    2019-01-31
    看了一下源码,Once是先将done值置为1后再执行的参数函数。所以应该不会阻塞等待函数执行的情况。
            if atomic.LoadUint32(&o.done) == 1 {
            return
        }
        // Slow-path.
        o.m.Lock()
        defer o.m.Unlock()
        if o.done == 0 {
            defer atomic.StoreUint32(&o.done, 1)
            f()
        }
    展开
     2
    
  • jacke
    2018-12-16
    问下为什么:Add方法和Wait方法的调用是在两个goroutine里面同时调用,Add会panic?
    wait不是在计数器为零的时候什么都不做吗?Add先执行后执行没问题啊
     1
    
  • 蔺晨
    2018-11-21
    func getAllGoroutineResult(){
            wg := sync.WaitGroup{}
            wg.Add(3)

            once := sync.Once{}
            var aAndb int
            var aStrAndb string
            var gflag int32

            addNum := func(a,b int, ret *int) {
                defer wg.Done()
                time.Sleep(time.Millisecond * 2000)
                *ret = a+b
                atomic.AddInt32(&gflag,1)
            }

            addStr := func(a,b string, ret *string) {
                defer wg.Done()
                time.Sleep(time.Millisecond * 1000)
                *ret = a+b
                atomic.AddInt32(&gflag,1)
            }

            // waitRet需要等待 addNum和addStr执行完成后的结果
            waitRet := func(ret *int, strRet *string) {
                defer wg.Done()
                once.Do(func() {
                    for atomic.LoadInt32(&gflag) != 2 {
                        fmt.Println("Wait: addNum & addStr")
                        time.Sleep(time.Millisecond * 200)
                    }
                })
                fmt.Println(fmt.Sprintf("AddNum's Ret is: %d\n", *ret))
                fmt.Println(fmt.Sprintf("AddStr's Ret is: %s\n", *strRet))
            }

            // waitRet goroutine等待AddNum和AddStr结束
            go waitRet(&aAndb, &aStrAndb)
            go addNum(10, 20, &aAndb)
            go addStr("测试结果", "满意不?", &aStrAndb)

            wg.Wait()
    }
    展开
    
    
  • Leon📷
    2018-11-20
    通过wait阻塞的协程的函数的参数传入指针,然后等wait()执行结束后,通过对应变量来收取值
    var wg sync.WaitGroup
    var a int
    wg.add(1)
    go func(a *int) {
      *a= 3
    }
    wg.Wait()
    使用a获取值
    是这样吗,老师
    展开
    
    
我们在线,来聊聊吧