如果包含 time.Sleep,Goroutine 不会执行

Posted

技术标签:

【中文标题】如果包含 time.Sleep,Goroutine 不会执行【英文标题】:Goroutine does not execute if time.Sleep included 【发布时间】:2015-04-03 04:08:21 【问题描述】:

以下代码运行良好:

package main

import (
    "fmt"
)

func my_func(c chan int)
    fmt.Println(<-c)


func main()
    c := make(chan int)
    go my_func(c)

    c<-3

playgound_1

如果我改变了

c<-3

time.Sleep(time.Second)
c<-3

playground_2

我的代码没有执行。

我的直觉是 mainmy_func 完成执行之前返回,但似乎添加暂停应该没有任何效果。我完全迷失在这个简单的例子中,这是怎么回事?

【问题讨论】:

【参考方案1】:

main 函数结束时,程序也随之结束。它不会等待其他 goroutine 完成。

引用Go Language Specification: Program Execution:

程序执行从初始化主包开始,然后调用函数main。当该函数调用返回时,程序退出。它不会等待其他(非main)goroutine 完成。

因此,当您的main 函数通过在通道上发送值而成功时,程序可能会立即终止,在另一个 goroutine 有机会将接收到的值打印到控制台之前。

如果要确保将值打印到控制台,则必须将其与退出 main 函数的事件同步:

以“完成”频道为例(在 Go Playground 上试用):

func my_func(c, done chan int) 
    fmt.Println(<-c)
    done <- 1


func main() 
    c := make(chan int)
    done := make(chan int)
    go my_func(c, done)

    time.Sleep(time.Second)
    c <- 3
    <-done

由于done 也是一个无缓冲通道,因此在main 函数结束时从它接收必须等待done 通道上的值的发送,这发生在通道c 上发送的值之后已收到并打印到控制台。

对看似不确定的运行的解释:

Goroutines 可能会或可能不会并行执行同时。同步确保某些事件在其他事件之前发生。这是您获得的唯一保证,也是您唯一应该依赖的东西。 2 个例子发生在之前

启动新 goroutine 的 go 语句发生在 goroutine 开始执行之前。 通道上的发送发生在来自该通道的相应接收完成之前。

更多详情请阅读The Go Memory Model

回到你的例子:

来自无缓冲通道的接收发生在该通道上的发送完成之前。

所以你得到的唯一保证是运行my_func()的goroutine将接收来自main()的通道c的值。但是一旦接收到值,main 函数可能 会继续,但由于发送后没有更多的语句,它只是与程序一起结束。非main 协程是否有时间机会 使用fmt.Println() 打印它未定义

【讨论】:

你能解释一下为什么在我的第一个示例中my_func 在主要结束之前完成,但在第二个示例中没有? 我认为这是正确的答案,但如果添加一些信息可能会更好:一种(或多种)让示例代码等待的方法...... 添加说明和示例如何确保在通道上发送的值被打印。 我想我明白了,但我想确定一下。我的理解是否正确:“第一个和第二个示例,我的 goroutine 可能会或可能无法在 main 完成之前完成,而它发生在第一个示例而不是第二个示例中的事实只是纯粹的机会。 @Akavall 是的,在这两种情况下,值可能会或可能不会被打印出来。请注意,在 Go Playground 上,程序的结果会被缓存。因此,如果在某种情况下它被打印出来,运行相同的程序总是会打印它(直到有人删除了程序的输出缓存)。

以上是关于如果包含 time.Sleep,Goroutine 不会执行的主要内容,如果未能解决你的问题,请参考以下文章

Go_goroutine和channel结合

Wait for a goroutine

Go语言入门goroutine和channel

在没有 time.sleep 的代码中添加时间延迟(代码包含线程)

Go 并发

无限for循环内的goroutine。这是一个好习惯吗?