为什么在这个通道上发送消息不会阻塞?

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

Why aren't sends on this channel blocking?

问题

考虑以下Go代码片段:

c := make(chan string)

go func() {
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Sending test1...")
    c <- "test1"
    fmt.Println("Sending test2...")
    c <- "test2"
    fmt.Println("Done sending")
}()

go func() {
    for {
        select {
        case n := <-c:
            fmt.Println("Received:", n)
        }
    }
}()

我预期第一个goroutine在尝试将"test1"写入通道时会被阻塞,直到第二个goroutine接收到数据。预期的输出应该是:

Sending test1...
Received: test1
Sending test2...
Received: test2

然而,当我实际运行这个示例时,输出结果却是:

Sending test1...
Sending test2...
Received: test1
Received: test2

从输出结果来看,发送操作似乎没有像预期的那样被阻塞。这是怎么回事呢?

英文:

Consider the following snippet of Go code:

c := make(chan string)

go func() {
	time.Sleep(100 * time.Millisecond)
	fmt.Println(&quot;Sending test1...&quot;)
	c &lt;- &quot;test1&quot;
	fmt.Println(&quot;Sending test2...&quot;)
	c &lt;- &quot;test2&quot;
	fmt.Println(&quot;Done sending&quot;)
}()

go func() {
	for {
		select {
		case n := &lt;-c:
			fmt.Println(&quot;Received:&quot;, n)
		}
	}
}()

<sub>Full source: http://play.golang.org/p/yY6JQzDMvn&lt;/sub>

I expect that the first goroutine will block when attempting to write &quot;test1&quot; to the channel until the receive in the second goroutine. The expected output would be:

Sending test1...
Received: test1
Sending test2...
Received: test2

However, when I actually run the example, I end up with:

Sending test1...
Sending test2...
Received: test1
Received: test2

It would appear by the output that the send is not blocking as one would expect. What's going on here?

答案1

得分: 5

这是由两个goroutine中的竞争条件引起的。默认情况下,Go运行时使用单个线程来处理所有的goroutine,因此执行是串行的。考虑以下可能的代码执行情况:

  • 第一个goroutine被Sleep()调用阻塞
  • 第二个goroutine在通道上等待接收
  • 第一个goroutine在Sleep()调用结束后恢复执行
  • &quot;test1&quot;被写入通道,导致阻塞
  • 第二个goroutine接收到值,但在打印输出之前,执行切换回第一个goroutine
  • 第一个goroutine打印&quot;Sending test2...&quot;并将第二个值写入通道,再次阻塞
  • 第二个goroutine在被抢占的位置继续执行,并打印&quot;Received: test1&quot;消息
  • 循环再次执行并接收第二个值

总结起来,发送操作确实是阻塞的,只是由于输出的顺序问题看起来不是这样。

英文:

This is caused by a race condition in the two goroutines. By default, the Go runtime uses a single thread for all goroutines, so execution is serialized. Consider the following possible scenario for executing the code above:

  • the first goroutine is blocked by a Sleep() call
  • the second goroutine is blocked waiting for a receive on the channel
  • the first goroutine resumes execution after the Sleep() call ends
  • &quot;test1&quot; is written to the channel, which blocks
  • the second goroutine receives the value but before printing the output, execution switches back to the first goroutine
  • the first goroutine prints &quot;Sending test2...&quot; and writes the second value to the channel which again blocks
  • the second goroutine resumes where it was preempted and prints the &quot;Received: test1&quot; message
  • the loop is executed again and receives the second value

In summary, the send is indeed blocking, it just doesn't appear to be due to the order of output.

huangapple
  • 本文由 发表于 2015年5月29日 06:33:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/30518166.html
匿名

发表评论

匿名网友

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

确定