为什么从未达到返回语句?

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

Why never reached the return statement

问题

请注意以下代码片段:

package main

import (
	"fmt"
	"time"
)

func sender(ch chan string) {

	ch <- "Hello"
	ch <- "Foo"
	ch <- "and"
	ch <- "Boo"
	close(ch)
}

func main() {

	ch := make(chan string)

	go sender(ch)

	for {
		select {
		case value := <-ch:
			fmt.Println(value)
		case <-time.After(time.Second * 2):
			fmt.Println("Return")
			return
		}
	}
}

作为结果,我得到了空白输出,并且 time.After 永远不会被触发。为什么会这样?

我注意到,当我尝试从一个已关闭的通道接收值时,它会接收到该类型的零值。为什么我仍然可以从一个已关闭的通道接收值?

我还可以像这样进行检查:

v, ok := <-ch

如果 okfalse,则表示通道已关闭。

英文:

Look at the following code snippet

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func sender(ch chan string) {

	ch &lt;- &quot;Hello&quot;
	ch &lt;- &quot;Foo&quot;
	ch &lt;- &quot;and&quot;
	ch &lt;- &quot;Boo&quot;
	close(ch)
}

func main() {

	ch := make(chan string)

	go sender(ch)

	for {
		select {
		case value := &lt;-ch:
			fmt.Println(value)
		case &lt;-time.After(time.Second * 2):
			fmt.Println(&quot;Return&quot;)
			return
		}
	}
}

As result I've got blank output and the time.After will be never reached. Why?

I notice, when I try to receive value from a closed channel, it will receive the zero value from the type. Why can I still receive value from a closed channel?

I can check also like is too,

v, ok := &lt;-ch

if ok is false, the channel is closed.

答案1

得分: 4

在每次for循环迭代时,都会创建一个新的两秒计时器。关闭的通道始终准备好接收。代码会一直循环,因为在关闭的通道准备好接收之前,新计时器的通道永远不会准备好接收。

修复问题的一种方法是将通道设置为nil:

case value, ok := <-ch:
    if !ok {
        ch = nil
    } else {
        fmt.Println(value)
    }

对于nil通道的接收永远不会准备好。

playground示例

如果你希望循环最多运行两秒钟,那么你应该在循环外创建计时器:

after := time.After(time.Second * 2)

并在循环中选择这个计时器:

case <-after:
    fmt.Println("Return")
    return

playground示例(添加了sleep以在playground上运行示例)

你可以将将通道设置为nil和在循环外创建计时器结合起来。

playground示例

英文:

A new two second timer is created on every iteration of the for loop. Closed channels are always ready to receive. The code loops forever because the channel for the new timer is never ready to receive before the closed channel is ready to receive.

One way to fix the problem is to set the channel to nil:

	case value, ok := &lt;-ch:
		if !ok {
			ch = nil
		} else {
			fmt.Println(value)
		}

Receive on a nil channel is never ready.

playground example

If you wanted the loop to run for at most two seconds, then you should create the timer outside of the loop:

	after := time.After(time.Second * 2)

and select on this one timer in the loop:

	case &lt;-after:
		fmt.Println(&quot;Return&quot;)
		return

playground example (sleep added to make example run on playground)

You can combine setting the channel to nil and creating the timer outside of the loop.

playground example

huangapple
  • 本文由 发表于 2015年1月16日 14:59:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/27978799.html
匿名

发表评论

匿名网友

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

确定