Go 代码示例未选择“完成”情况?

改编为以下脚本:

package main


import (

    "fmt"

    "time"

)


func main() {

    ticker := time.NewTicker(500 * time.Millisecond)

    done := make(chan bool)


    go func() {

        for {

            select {

            case <-done:

                fmt.Println("Received 'done'")

                return

            case t := <-ticker.C:

                fmt.Println("Tick at", t)

            }

        }

    }()


    time.Sleep(1600 * time.Millisecond)

    // ticker.Stop()

    done <- true

    // fmt.Println("Ticker stopped.")

}

与引用示例的两个区别是我注释掉了该ticker.Stop()行并fmt.Println("Received 'done'")在case <-done块中添加了一行。如果我运行它,我会观察到以下输出:


> go run tickers.go

Tick at 2019-10-06 15:25:50.576798 -0700 PDT m=+0.504913907

Tick at 2019-10-06 15:25:51.074993 -0700 PDT m=+1.003102855

Tick at 2019-10-06 15:25:51.576418 -0700 PDT m=+1.504521538

我的问题:为什么它不打印Received 'done'到终端?


奇怪的是,如果我在Ticker stoppedPrintln 语句中发表评论,我也会看到Received 'done':


> go run tickers.go

Tick at 2019-10-06 15:27:30.735163 -0700 PDT m=+0.504666656

Tick at 2019-10-06 15:27:31.234076 -0700 PDT m=+1.003573649

Tick at 2019-10-06 15:27:31.735342 -0700 PDT m=+1.504833296

Ticker stopped.

Received 'done'

我记得,Goroutine 中的代码可以假设同步运行,所以我很困惑,因为我没有看到Println前一种情况下语句的效果,因为它发生在 Goroutine 返回之前。有人可以解释一下吗?


去并发性协程


GCT1015
浏览 83回答 2
2回答

慕森王

发生时done <- true,main函数直接返回。您可以添加另一个 time.Sleep() 来看看发生了什么。&nbsp; &nbsp; time.Sleep(1600 * time.Millisecond)&nbsp; &nbsp; // ticker.Stop()&nbsp; &nbsp; done <- true&nbsp; &nbsp; // fmt.Println("Ticker stopped.")&nbsp; &nbsp; time.Sleep(1600 * time.Millisecond)

SMILET

...为什么它不将 Received 'done' 打印到终端?它确实这样做了——或者更确切地说,它尝试了。当主 goroutine(调用mainpackage main)返回时(或者在此处未发生的各种情况下更早),Go 程序就会退出。您致电并发送后main返回。time.Sleep()truedone同时,当值到达通道时,循环中的 goroutinefor就会被唤醒。这是在主 Goroutine 发送之后发生的,之后主 Goroutine 正在退出。truedone如果在这个退出过程中,主协程花费的时间足够长,那么匿名协程就有时间打印Received 'done'。如果在这个退出过程中,主 Goroutine 足够快,那么匿名 Goroutine 永远不会完成,或者甚至永远不会开始,打印任何东西,你什么也看不到。(实际输出是由单个底层系统调用完成的,因此您要么获得全部输出,要么什么也得不到。)您可以通过多种机制确保您派生的 goroutine 在主协程退出之前完成,但最简单的可能是使用,sync.WaitGroup因为它就是为此设计的。创建一个 waitgroup,将其计数器设置为 1(将 1 添加到其初始零),然后Done在退出匿名 goroutine 时调用该函数。让主协程等待它。
打开App,查看更多内容
随时随地看视频慕课网APP