等待 goroutine 完成的正确方法

Posted

技术标签:

【中文标题】等待 goroutine 完成的正确方法【英文标题】:proper way of waiting for a go routine to finish 【发布时间】:2014-11-15 06:17:41 【问题描述】:

我想知道在退出程序之前是什么。阅读其他一些答案,bool chan 似乎可以解决问题,如Playground link

func do_stuff(done chan bool) 
    fmt.Println("Doing stuff")
    done <- true


func main() 
    fmt.Println("Main")
    done := make(chan bool)
    go do_stuff(done)
    <-done
    //<-done

我有两个问题:

为什么

如果我取消最后一行的注释会发生什么?我有一个死锁错误。这是因为通道是空的,没有其他函数向它发送值吗?

【问题讨论】:

&lt;- done 有效,因为这正是频道所做的 :)(有关详细信息,请参阅 golang.org/ref/spec#Receive_operator 和 golang.org/ref/spec#Send_statements)。关于取消注释第二行如何导致僵局,您是对的。没有任何东西发送到 done 并且 Go 认识到,如果它等待,甚至没有任何东西 可以(例如,没有任何东西在等待网络调用)。 【参考方案1】:

为什么&lt;- done 完全有效?

之所以有效,是因为运行时检测到您正在向其他地方的通道写入内容。

如果我取消最后一行的注释会怎样?

运行时足够聪明,可以知道没有任何其他内容正在写入并且它会死锁。

奖励,如果你的内存非常有限,你可以使用done := make(chan struct)done &lt;- structstruct 保证使用 0 内存。

【讨论】:

你错过了否定吗? “足够聪明”+“死锁”——我认为如果它很聪明,它就不会死锁,而是会给你一个错误,说它会死锁。所以在我看来,要么运行时不够聪明,要么它没有死锁。是哪个? 它足够聪明,因为它会给你一个错误,告诉你它已经死锁了。【参考方案2】:

收听频道&lt;- done,是一个阻塞操作,所以你的程序将不会继续,直到发送真或假,即done &lt;- true

根据具体情况,您的问题可能会有不同的答案。

例如,假设您想要并行化一系列需要很长时间的函数调用。

我会为此使用 sync

package main

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

func main() 
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ 
        wg.Add(1)
        go func() 
            longOp()
            wg.Done()
        ()
    
    // will wait until wg.Done is called 10 times
    // since we made wg.Add(1) call 10 times
    wg.Wait()


func longOp() 
    time.Sleep(time.Second * 2)
    fmt.Println("long op done")

【讨论】:

以上是关于等待 goroutine 完成的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 time.Sleep 的情况下等待所有 goroutines 完成?

如何等待多个 goroutine 完成?

等待多个 goroutine 的结果

第三章 Goroutine调度策略(16)

Go sync.WaitGroup 等待Goroutine执行完成

Go语言WaitGroup