go routine for range over channels
Solution 1
This situation caused of output channel of sq
function is not buffered. So sq
is waiting until next function will read from output, but if sq
is not async, it will not happen (Playground link):
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
numsCh := gen(3, 4)
sqCh := sq(numsCh) // if there is no sq in body - we are locked here until input channel will be closed
result := sq(sqCh) // but if output channel is not buffered, so `sq` is locked, until next function will read from output channel
for n := range result {
fmt.Println(n)
}
fmt.Println("Process completed")
}
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int, 100)
for n := range in {
out <- n * n
}
close(out)
return out
}
Solution 2
Your function creates a channel, writes to it, then returns it. The writing will block until somebody can read the corresponding value, but that's impossible because nobody outside this function has the channel yet.
func sq(in <-chan int) <-chan int {
// Nobody else has this channel yet...
out := make(chan int)
for n := range in {
// ...but this line will block until somebody reads the value...
out <- n * n
}
close(out)
// ...and nobody else can possibly read it until after this return.
return out
}
If you wrap the loop in a goroutine then both the loop and the sq
function are allowed to continue; even if the loop blocks, the return out
statement can still go and eventually you'll be able to connect up a reader to the channel.
(There's nothing intrinsically bad about looping over channels outside of goroutines; your main
function does it harmlessly and correctly.)
Related videos on Youtube
Comments
-
Himanshu almost 2 years
I have been working in Golang for a long time. But still I am facing this problem though I know the solution to my problem. But never figured out why is it happening.
For example If I have a pipeline situation for inbound and outbound channels like below:
package main import ( "fmt" ) func main() { for n := range sq(sq(gen(3, 4))) { fmt.Println(n) } fmt.Println("Process completed") } func gen(nums ...int) <-chan int { out := make(chan int) go func() { for _, n := range nums { out <- n } close(out) }() return out } func sq(in <-chan int) <-chan int { out := make(chan int) go func() { for n := range in { out <- n * n } close(out) }() return out }
It does not give me a deadlock situation. But if I remove the go routine inside the outbound code as below:
func sq(in <-chan int) <-chan int { out := make(chan int) for n := range in { out <- n * n } close(out) return out }
I received a deadlock error. Why is it so that looping over channels using range without go routine gives a deadlock.
-
Motti over 5 yearsI remove the goroutine in
sq
which was what was actually asked about (your answer still solves the problem). Please re-edit if I changed your intent. -
Himanshu over 5 yearsThis does not provide my answer. My question was why we are not getting deadlock on wrapping the inbound channels inside go routine. This has nothing to do with the buffer. In buffered channels the channel will add the data to the buffer when receiver is not available.
-
Himanshu over 5 yearsThanks but I am still not getting it. I know the reason behind the deadlock. But using go routine will let me range over channels. And one more question comes to my mind now why is it working in the main. It will be appreciated if you provide an reference example for the reason behind do's and don't's.
-
Adrian over 5 yearsYou're not getting a deadlock with a goroutine because the writes are in the goroutine. That goroutine blocks, the outer function returns the channel, the caller of that func starts reading from it, and the inner goroutine is then able to send on it.
-
Himanshu over 5 years@Adrian thanks for your comment but It is our assumption. It is not mentioned anywhere inside the docs. Also If I knew that it will help my or anyone in future to know how actually channels wait even if there is no buffer or no value is coming from other end. I have experimented a lot. For example have a look at this play.golang.org/p/owLc5QoAROa.
-
Adrian over 5 yearsThe relevant facts are definitely mentioned, ie that channel operations can block, and that goroutines run concurrently. Your specific example is just an application of those fundamentals.
-
iHelos over 5 yearsit's all about different flows of sync and async programms. in case of 'go' keyword usage the program can transfer control of program execution to another goroutine, so buffer can be freed.
Sync:
1) Write to channel
2) Write to channel - blocked
and the async usage can be likeAsync:
1) Write to channel
2) Read from channel
3) Write to channel
4) Read from channel