为什么在第一个案例实际执行时,这个选择总是运行默认情况?

时间:2017-08-01 19:43:40

标签: select go channels

我正在努力更好地了解golang频道。在阅读this article时,我正在使用非阻塞发送,并提出以下代码:

package main
import (
    "fmt"
    "time"
)

func main() {
    stuff := make(chan int)
    go func(){
        for i := 0; i < 5; i ++{
            select {
            case stuff <- i:
                fmt.Printf("Sent %v\n", i)
            default:
                fmt.Printf("Default on %v\n", i)
            }
        }
        println("Closing")
        close(stuff)
    }()
    time.Sleep(time.Second)
    fmt.Println(<-stuff)
    fmt.Println(<-stuff)
    fmt.Println(<-stuff)
    fmt.Println(<-stuff)
    fmt.Println(<-stuff)
}

这将打印:

Default on 0
Default on 1
Default on 2
Default on 3
Default on 4
Closing
0
0
0
0
0

虽然我明白只会打印0,但我真的不明白为什么第一次发送仍会触发选择的default分支?

在这种情况下,选择行为背后的逻辑是什么?

Example at the Go Playground

4 个答案:

答案 0 :(得分:4)

您永远不会向stuff发送任何值,在执行fmt.Println语句中的任何接收操作之前执行所有默认情况。如果没有其他操作可以继续,则立即采用default情况,这意味着您的循环将尽快执行并返回。

您想要阻止循环,因此您不需要default案例。您最后也不需要close,因为您不依赖于已关闭的频道取消阻止接收或违反range子句。

stuff := make(chan int)
go func() {
    for i := 0; i < 5; i++ {
        select {
        case stuff <- i:
            fmt.Printf("Sent %v\n", i)
        }
    }
    println("Closing")
}()
time.Sleep(time.Second)
fmt.Println(<-stuff)
fmt.Println(<-stuff)
fmt.Println(<-stuff)
fmt.Println(<-stuff)
fmt.Println(<-stuff)

https://play.golang.org/p/k2rmRDP38f

另请注意,最后的“已发送”和“结束”行未打印,因为您没有其他同步等待goroutine完成,但这不会影响此示例的结果。

答案 1 :(得分:2)

它只执行默认情况,因为for循环在从通道开始读取任何内容之前运行5次。每次通过,因为没有任何东西可以从通道读取,它将进入默认情况。如果某些东西可以从频道中读取,它就会执行该情况。

答案 2 :(得分:2)

由于您正在使用非阻止&#39;发送&#39;,如果读者已经在等待阅读频道上的内容,stuff <- i将真正执行,或者如果通道有一些缓冲区。如果没有,请发送&#39;必须阻止。

现在,因为在从通道读取的打印语句之前有time.Sleep(time.Second),所以直到1秒后才会有通道的读者。另一方面,goroutine在那段时间内完成执行,并且不会发送任何内容。

您在输出中看到全部为零,因为fmt.Println(...)语句正在从已关闭的频道中读取。

答案 3 :(得分:1)

你的第一个案子没有执行。

以下是您的计划的作用:

  1. 启动goroutine。
  2. 尝试在频道上发送04,这些频道全部阻止,因为没有任何内容正在阅读频道,因此会陷入默认状态。
  3. 与此同时,在主要的goroutine中,你正在睡觉一秒钟......
  4. 然后在第二个过去之后,尝试从频道中读取,但它已关闭,因此您每次都会获得0
  5. 要获得理想的行为,您有两种选择:

    1. 使用缓冲通道,它可以保存您发送的所有数据:

      stuff := make(chan int, 5)
      
    2. 不要在select语句中使用default,这会导致每个发送都等到它成功。

    3. 哪个首选取决于您的目标。对于像这样的最小例子,要么可能没有更好或更差。