duandai2178 2019-01-11 18:33
浏览 78
已采纳

sync.Waitgroup不会阻止执行

Consider this code snippet

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    wg := new(sync.WaitGroup)
    nap := func() {
        wg.Add(1)
        time.Sleep(2 * time.Second)
        fmt.Println("nap done")
        wg.Done()
    }

    go nap()
    go nap()
    go nap()

    fmt.Println("nap time")
    wg.Wait()
    fmt.Println("all done")
}

Running such code gives expected output:

nap time
nap done
nap done
nap done
all done

Now let's ommit first standard output print before wg.Wait():

// fmt.Println("nap time")
wg.Wait()
fmt.Println("all done")

The output now changes to unexpected:

all done

Where expected would be:

nap done
nap done
nap done
all done

Same code on the playground does give this output without a need of omitting the stdout print.

Can you explain to me, what I am missing there?

  • 写回答

1条回答 默认 最新

  • duanniedang3946 2019-01-11 18:33
    关注

    Even though this looks like magic, it has a logical explanation. Go doesn't guarantee the order of goroutines execution. There are three goroutines spawned in given snippet code, but in fact, there are four of them: the very first one that is spawned when the execution starts.

    Stdout print is omitted

    This goroutine spawned three nap functions and continued in its plan. It was so fast that it executed wg.Wait() before any of spawned goroutines was able to call wg.Add(1). As a result wg.Wait() didn't block the execution and program ended.

    Print to stdout before wg.Wait()

    In this case, program execution was different, goroutines were able to make wg.Add(1) call because the main goroutine wasn't fast as in the first case. This behavior isn't guaranteed, which can be seen in the linked playground example.

    It has nothing to do with stdout print

    The following code sample will give the same expected output:

    time.Sleep(time.Second)
    wg.Wait()
    fmt.Println("all done")
    

    The fmt.Println() had the same impact as time.Sleep() had.

    Idiomatic way

    The rule is simple: call wg.Add(1) before spawning the goroutine.

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    func main() {
        wg := new(sync.WaitGroup)
        nap := func() {
            time.Sleep(2 * time.Second)
            fmt.Println("nap done")
            wg.Done()
        }
    
        napCount := 3
        wg.Add(napCount)
        for i := 0; i < napCount; i++ {
            go nap()
        }
    
        wg.Wait()
        fmt.Println("all done")
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 merge函数占用内存过大
  • ¥15 Revit2020下载问题
  • ¥15 使用EMD去噪处理RML2016数据集时候的原理
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大
  • ¥15 单片机无法进入HAL_TIM_PWM_PulseFinishedCallback回调函数
  • ¥15 Oracle中如何从clob类型截取特定字符串后面的字符
  • ¥15 想通过pywinauto自动电机应用程序按钮,但是找不到应用程序按钮信息
  • ¥15 如何在炒股软件中,爬到我想看的日k线
  • ¥15 seatunnel 怎么配置Elasticsearch
  • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.