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

Goroutine does not execute if time.Sleep included

以下代码运行良好:

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

我的代码没有执行。

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

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

引自Go Language Specification: Program Execution

Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

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

如果您想确保该值被打印到控制台,您必须将它与退出 main 函数的事件同步:

带有 "done" 频道的示例(在 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 个例子:

  • The go statement that starts a new goroutine happens before the goroutine's execution begins.
  • A send on a channel happens before the corresponding receive from that channel completes.

有关详细信息,请阅读 The Go Memory Model

回到你的例子:

A receive from an unbuffered channel happens before the send on that channel completes.

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