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

Aka*_*all 9 concurrency channel go goroutine

以下代码运行完全正常:

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
}
Run Code Online (Sandbox Code Playgroud)

playgound_1

但是,如果我改变

c<-3
Run Code Online (Sandbox Code Playgroud)

time.Sleep(time.Second)
c<-3
Run Code Online (Sandbox Code Playgroud)

playground_2

我的代码没有执行.

我的直觉是mainmy_func完成执行之前以某种方式返回,但似乎添加暂停应该没有任何效果.我完全迷失在这个简单的例子上,这里发生了什么?

icz*_*cza 12

main函数结束时,程序以它结束.它不等待其他goroutines完成.

引用Go语言规范:程序执行:

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

因此,当您的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
}
Run Code Online (Sandbox Code Playgroud)

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

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

够程可能会或可能不会被并行执行在同一时间.同步可确保某些事件在其他事件发生之前发生.这是您获得的唯一保证,也是您应该依赖的唯一保证.这两个例子发生之前:

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

有关更多详细信息,请参阅Go Memory Model.

回到你的例子:

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

因此,唯一可以保证的是,运行的goroutine my_func()将从c发送的通道接收值main().但是一旦收到该值,该main函数可能会继续,但由于发送后没有更多的语句,它只是结束 - 与程序一起.无论非main够程将有时间机会与打印fmt.Println()没有定义.

  • @Akavall是的,在这两种情况下,可能会或可能不会打印值.请注意,在Go Playground上,程序的结果会被缓存.因此,如果在一种情况下它被打印,运行相同的程序将始终打印它(直到有人删除程序的输出缓存). (4认同)
  • 你能解释一下为什么在我的第一个例子中`my_func`在主要结束之前结束,但在第二个例子中没有结束? (2认同)