I tried to find a similar question but I couldn't, so here I am asking:
I'm using close(ch)
inside a recursive function. I need to close channel to terminate a range
loop. However, since the function is recursive, the close
is run multiple times which gives me:
panic: close of closed channel
If I comment-out the close(ch)
statement, I receive:
fatal error: all goroutines are asleep - deadlock!
Please find below my code:
package main
import "golang.org/x/tour/tree"
import "fmt"
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
ch<-(t.Value)
if t.Left != nil { Walk(t.Left, ch) }
if t.Right != nil { Walk(t.Right, ch) }
close(ch) // => How to close a channel inside recursive function? ***
return
}
func main() {
ch := make(chan int)
go Walk(tree.New(1), ch)
for i := range ch {
fmt.Print(i, " ")
}
}
Stopping Condition for Recursion If we don't mention any condition to break the recursive call, the function will keep calling itself infinitely. We use the if...else statement (or similar approach) to break the recursion. Normally, a recursive function has two branches: One for recursive calls.
You break out of recursion by having conditions under which you simply don't perform new recursive calls, and making sure that those conditions are eventually always met. You can write a recursive function that contains pass : def fac(n):
It's OK to leave a Go channel open forever and never close it. When the channel is no longer used, it will be garbage collected. Note that it is only necessary to close a channel if the receiver is looking for a close. Closing the channel is a control signal on the channel indicating that no more data follows.
Close the channel outside of the recursive function:
func Walk(t *tree.Tree, ch chan int) {
ch<-(t.Value)
if t.Left != nil { Walk(t.Left, ch) }
if t.Right != nil { Walk(t.Right, ch) }
}
func main() {
ch := make(chan int)
go func() {
defer close(ch)
Walk(tree.New(1), ch)
}()
for i := range ch {
fmt.Print(i, " ")
}
}
Edit: This answer follows a common Go idiom of using defer
for cleanup actions. As comments have noted, the defer
is not necessary. The body of the anonymous function can also be written as:
Walk(tree.New(1), ch)
close(ch)
Cerise Limón's answer works fine. I just wanted to mention that it's not mandatory to use defer
inside the closure:
package main
import (
"fmt"
"golang.org/x/tour/tree"
)
func Walk(t *tree.Tree, ch chan int) {
if t.Left != nil { Walk(t.Left, ch) }
ch <- t.Value
if t.Right != nil { Walk(t.Right, ch) }
}
func main() {
ch := make(chan int)
go func() {
Walk(tree.New(1), ch)
close(ch)
}()
for i := range ch {
fmt.Print(i, " ")
}
}
After the Walk
function finishes, the channel is closed, and both actions are performed in a separated goroutine.
If we use
go Walk(tree.New(1), ch)
close(ch)
We are starting a goroutine but closing the channel immediately in the current one.
Since I wanted to close the channel inside the recursive function, I ended up doing this:
func Walk(t *tree.Tree, ch chan int, closeAtTheEnd bool) {
if t.Left != nil {
// fmt.Println("Walk the Left of", t.Value)
Walk(t.Left, ch, false)
}
// fmt.Println("Send to the channel:", t.Value)
ch <- t.Value
if t.Right != nil {
// fmt.Println("Walk the Right of", t.Value)
Walk(t.Right, ch, false)
}
if closeAtTheEnd {
close(ch)
}
}
That way, recursive calls won't close the channel, and only the first call of Walk
is allowed to do that (the tree starts at the root node and closes the channel after walking both directions).
func useWalkAndPrintTreeValues(k int) {
c := make(chan int, 10)
go Walk(tree.New(k), c, true)
for x := range c {
fmt.Print(x, " ")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With