I am new to Go an experimenting with channels and goroutines. I want to traverse a binary tree recursively using different goroutines, but can only get the first value to print out, and don't understand why. If I don't close the channel I'm using, all values print out, but then I get a threadlock issue. I also tried using two different channels but couldn't make that work either. Any help would be appreciated.
package main
import "golang.org/x/tour/tree"
import "fmt"
// Walk walks the tree t sending all values
// from the tree to the channel resCh.
func Walk(t *tree.Tree, resCh chan int) {
resCh <- t.Value
if t.Left==nil && t.Right==nil {
return
}
if t.Left != nil {
go Walk(t.Left, resCh)
}
if t.Right!=nil {
go Walk(t.Right, resCh)
}
}
func main() {
resCh:= make(chan int, 10)
Walk(tree.New(1), resCh)
<-resCh
close(resCh)
for i := range resCh {
fmt.Println(i)
}
}
Let me start by saying thank you for a minimal reproducer. Far too many people don't include such an example of what they're trying to do. The one tiny problem with your example was you didn't make it crystal clear that either these two lines
<-resCh
close(resCh)
or this block should be included but not both:
for i := range resCh {
fmt.Println(i)
}
Without the close(resCh)
a deadlock occurs because the the range
operator will eventually block waiting for more data. Since there are no other running goroutines the Go runtime notices that the program will neither terminate nor do any further work. Hence the deadlock error. The solution is to close the channel at the bottom of the Walk
function. You should also run Walk
in a separate goroutine rather than depend on the channel being able to buffer enough data to keep from causing a different type of deadlock.
Since the recursive calls don't actually do any work you don't need to run them in new goroutines unless you really want the indeterminism that introduces.
Below is one solution that mostly retains the structure of your original solution. Obviously there are other ways of doing this depending on the type of traversal desired.
package main
import (
"fmt"
"golang.org/x/tour/tree"
)
// Walk walks the tree t sending all values
// from the tree to the channel resCh.
func WalkBranch(t *tree.Tree, resCh chan int) {
resCh <- t.Value
if t.Left == nil && t.Right == nil {
return
}
if t.Left != nil {
WalkBranch(t.Left, resCh)
}
if t.Right != nil {
WalkBranch(t.Right, resCh)
}
}
func Walk(t *tree.Tree, resCh chan int) {
WalkBranch(t, resCh)
close(resCh)
}
func main() {
resCh := make(chan int, 2)
go Walk(tree.New(1), resCh)
for i := range resCh {
fmt.Println(i)
}
}