Goroutine在包含time.Sleep时不会执行。

huangapple go评论85阅读模式
英文:

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
}

playground_1

然而,如果我将

c<-3

改为

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

playground_2

我的代码就无法执行。

我的直觉告诉我,一些方式上 mainmy_func 执行完之前就返回了,但是似乎添加一个暂停不应该有任何影响。对于这个简单的例子,我完全迷失了,这里发生了什么?

英文:

The following code runs perfectly fine:

package main

import (
	&quot;fmt&quot;
)

func my_func(c chan int){
	fmt.Println(&lt;-c)
}

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

	c&lt;-3
}

playgound_1

However if I change

c&lt;-3

to

time.Sleep(time.Second)
c&lt;-3

playground_2

My code does not execute.

My gut feeling is that somehow main returns before the my_func finishes executing, but it seems like adding a pause should not have any effect. I am totally lost on this simple example, what's going on here?

答案1

得分: 15

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

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

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

因此,当你的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上的值之后。

对于看似非确定性运行的解释:

Goroutine可能会并行执行,也可能不会在同一时间执行。同步确保某些事件在其他事件之前发生。这是你所能得到的唯一保证,也是你应该依赖的唯一事情。
以下是两个示例的_Happens Before_:

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

更多详细信息请阅读Go内存模型

回到你的示例:

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

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

英文:

When the main function ends, the program ends with it. It does not wait for other goroutines to finish.

Quoting from the 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.

So simply when your main function succeeds by sending the value on the channel, the program might terminate immediately, before the other goroutine has the chance to print the received value to the console.

If you want to make sure the value gets printed to the console, you have to synchronize it with the event of exiting from the main function:

Example with a "done" channel (try it on Go Playground):

func my_func(c, done chan int) {
	fmt.Println(&lt;-c)
	done &lt;- 1
}

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

	time.Sleep(time.Second)
	c &lt;- 3
	&lt;-done
}

Since done is also an unbuffered channel, receiving from it at the end of the main function must wait the sending of a value on the done channel, which happens after the value sent on channel c has been received and printed to the console.

Explanation for the seemingly non-deterministic runs:

Goroutines may or may not be executed parallel at the same time. Synchronization ensures that certain events happen before other events. That is the only guarantee you get, and the only thing you should rely on.
2 examples of this Happens Before:

> - 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.

For more details read The Go Memory Model.

Back to your example:

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

So the only guarantee you get is that the goroutine that runs my_func() will receive the value from channel c sent from main(). But once the value is received, the main function may continue but since there is no more statements after the send, it simply ends - along with the program. Whether the non-main goroutine will have time or chance to print it with fmt.Println() is not defined.

huangapple
  • 本文由 发表于 2015年2月4日 04:07:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/28307783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定