问题
I am trying to play around with goroutines and channel
package main
import (
"fmt"
"math/rand"
"time"
)
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c
}
func main() {
c := fanInNew(boring("joe"), boring("anh"))
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
fmt.Println("You both are boring, I am leaving")
}
func fanInNew(input1, input2 <-chan string) <-chan string {
c := make(chan string)
for {
select {
case s := <-input1:
c <- s
case s := <-input2:
c <- s
}
}
return c
}
If i run this program it is giving me error, all goroutines are asleep, deadlock.
But If I put select
inside anonymous goroutine, it works just fine. Working example:
package main
import (
"fmt"
"math/rand"
"time"
)
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c
}
func main() {
c := fanInNew(boring("joe"), boring("anh"))
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
fmt.Println("You both are boring, I am leaving")
}
func fanInNew(input1, input2 <-chan string) <-chan string {
c := make(chan string)
go func() {
for {
select {
case s := <-input1:
c <- s
case s := <-input2:
c <- s
}
}
}()
return c
}
Can you please help me to understand reasoning behind it.
回答1:
The for
statements loops forever, and so the <-c
chan is never passed along and the chans get filled, but the main
thread gets stuck waiting for c := fanInNew(a, b)
.
fanInNew()
never returns because for
loops forever (and blocks on select
btw):
func fanInNew(input1, input2 <-chan string) <-chan string {
c := make(chan string)
for { // Loop forever and read from inputs 1 and 2
select {
case s := <-input1:
c <- s
case s := <-input2:
c <- s
}
}
return c
}
Then in the main thread this function never returns the c
chan.
func main() {
// Never gets passed the next line
c := fanInNew(boring("joe"), boring("anh"))
}
So you can put the for
loops themselves in goroutines, as you did in the second example. Also typically goroutines should return, either because you pass a message in (such as by close()
ing), or because they reach a return statement.
In any case, what you have in the 2nd example is great for demonstrating the use of anonymous closures. The chan
passed into the goroutine can be returned elsewhere and enable safe message passing between threads:
c := make(chan string)
go func() {
for {
select {
case s := <-input1:
c <- s
case s := <-input2:
c <- s
}
}
}()
return c
There are a few ways of ending a for loop in an anonymous goroutine, including select on a second chan
, a closing channel, which when close()
ed you can return. Also, typically WaitGroups can achieve that.
来源:https://stackoverflow.com/questions/41653695/all-goroutines-are-asleep-deadlock