以下代码运行良好:
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
游乐场_2
我的代码无法执行。
我的直觉是main在my_func完成执行之前,某种方式会返回,但是似乎添加暂停应该没有任何效果。我对这个简单的例子完全迷失了,这是怎么回事?
main
my_func
当main函数结束时,它的程序结束。它不等待其他goroutine完成。
引用Go语言规范:程序执行:
程序执行首先初始化主程序包,然后调用函数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是在接收到通道上发送的值并将其打印到控制台后发生的。
done
c
对于看似不确定的运行的解释:
够程可能会或可能不会被并行执行 在同一时间 。同步可确保某些事件先于其他事件发生。那是您获得的唯一保证,也是您应该依靠的唯一东西。 发生以下 2个示例:
go启动新的goroutine 的语句发生在goroutine的执行开始之前。 通道上的发送发生在该通道上的相应接收完成之前。
go
有关更多详细信息,请阅读 Go Memory Model 。
回到您的示例:
来自未缓冲通道的接收发生在该通道上的发送完成之前。
因此,您获得的唯一保证是,运行的goroutine my_func()将从c发送的通道接收值main()。但是,一旦接收到该值,该main函数就 可以 继续执行,但是由于发送后没有更多的语句,因此它仅随程序一起结束。无论非main够程将有 时间 或 机会 与打印fmt.Println()是 没有定义 。
my_func()
main()
fmt.Println()