Select 仅从一个通道打印输出

Select prints output from only one channel

我正在学习围棋,现在正在上频道。我用通道写了一个简单的程序。我创建了两个通道,通道被传递给一个被同时调用的函数。

我的期望是从两个通道打印输出,但实际上只打印一个通道输出:

package main

import "fmt"

func square(dat int, ch chan<- int) {

    ch <- dat * dat

}

func main() {

    resp1 := make(chan int)
    resp2 := make(chan int)

    go square(20, resp1)
    go square(10, resp2)

    select {
    case msg1 := <-resp1:
        fmt.Println(msg1)
    case msg2 := <-resp2:
        fmt.Println(msg2)
    }
}

在每次执行期间打印来自 resp1 的消息或来自 resp2 的消息。通道应该阻塞,直到有东西被推入其中,对吗?

The Go Programming Language Specification

Select statements

A "select" statement chooses which of a set of possible send or receive operations will proceed.

Select 从一组中选择一个。例如,

package main

import "fmt"

func square(dat int, ch chan<- int) {

    ch <- dat * dat

}

func main() {

    resp1 := make(chan int)
    resp2 := make(chan int)

    go square(20, resp1)
    go square(10, resp2)

    // Choose one
    select {
    case msg1 := <-resp1:
        fmt.Println(msg1)
    case msg2 := <-resp2:
        fmt.Println(msg2)
    }
    // Choose the other
    select {
    case msg1 := <-resp1:
        fmt.Println(msg1)
    case msg2 := <-resp2:
        fmt.Println(msg2)
    }
}

游乐场:https://play.golang.org/p/TiThqcXDa6o

输出:

100
400

您想要 select 和 select 两者?这与它的用途正好相反。来自 Go Spec:

A "select" statement chooses which of a set of possible send or receive operations will proceed.

If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.

如果您想从两个频道读取,而不是让 select 找出准备好从哪个频道读取(或者 select 一个 pseudo-randomly 如果两个频道都可以读取来自),不要使用 select。刚从两个阅读:

msg1 := <-resp1:
fmt.Println(msg1)
msg2 := <-resp2:
fmt.Println(msg2)

更新

如果正如@peterSO 所建议的那样,您的目标是从两个渠道读取数据,从先准备好的渠道开始,我认为这样的方法是合理的方法:

func main() {
    resp1 := make(chan int)
    resp2 := make(chan int)

    readsWanted := 0

    readsWanted += 1
    go square(20, resp1)
    readsWanted += 1
    go square(10, resp2)

    for i := 0; i < readsWanted; i++ {
        select {
        case msg1 := <-resp1:
            fmt.Println(msg1)
        case msg2 := <-resp2:
            fmt.Println(msg2)
        }
    }
}

你当然可以 hard-code 只循环 运行 两次,但我讨厌这样的事情,虽然在这个简单的例子中它并不重要。

根据您在 select 中的代码,只要任何情况匹配都将执行并且主函数将终止。这就是它仅打印单个通道值的原因。 您可以通过执行以下操作来解决此问题:

package main

import (
    "fmt"
    "os"
    "time"
)

func square(dat int, ch chan<- int) {
    ch <- dat * dat
}

func main() {
    resp1 := make(chan int)
    resp2 := make(chan int)

    go square(20, resp1)
    go square(10, resp2)
    time.Sleep(1 * time.Second)
    for {
        select {
        case msg1 := <-resp1:
            fmt.Println(msg1)
        case msg2 := <-resp2:
            fmt.Println(msg2)
        default:
            close(resp1)
            close(resp2)
            fmt.Println("no value recieved")
            os.Exit(0)
        }
    }
}

输出

100
400
no value recieved

在 playground 中查看:https://play.golang.org/p/T9mkfrO4wNF

选择在另一种语言中更像是一个开关(例如:Java),您需要根据需要执行该部分 N 次。