仅选择一个通道的打印输出
I am learning Go and I am on channels now. I have written a simple program using channels. I have created two channels and the channels are passed to a function which is called concurrently.
My expectation is to print output form both the channels but in reality only one channel output is getting printed:
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)
}
}
Either the message from resp1
is printed or that from resp2
during each execution. Channels should block until something is pushed into it, right?
The Go Programming Language Specification
A "select" statement chooses which of a set of possible send or receive operations will proceed.
Select chooses one of a set. For example,
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)
}
}
Playground: https://play.golang.org/p/TiThqcXDa6o
Output:
100
400
You want select
to select both? That's kind of the opposite of what it's for. From the 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.
If you want to read from both channels, rather than having select
figure out which one is ready to be read from (or select one pseudo-randomly if both can be read from), don't use select
. Just read from both:
msg1 := <-resp1:
fmt.Println(msg1)
msg2 := <-resp2:
fmt.Println(msg2)
Update
If, as @peterSO suggests, your goal is to read from both channels, starting with whichever is ready first, I'd think something like this would be a reasonable approach:
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)
}
}
}
You could of course hard-code the loop to only run twice, but I have an aversion to such things, although in this simple example it doesn't much matter.
As per your code in the select whenever any case matches will execute and main function will terminate .That is why it is only printing the single channel value. You can fix this by doing something like this :
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)
}
}
}
Output
100
400
no value recieved
See in playground: https://play.golang.org/p/T9mkfrO4wNF
Selects works more like a switch in another languages (ex: Java), you need to execute that part N times as you need.