You can see that approached detailed in "Go for gophers - GopherCon closing keynote - 25 April 2014 - Andrew Gerrand "
Stopping early
Add a quit
channel to the walker so we can stop it mid-stride.
func walk(t *tree.Tree, ch chan int, quit chan struct{}) {
if t.Left != nil {
walk(t.Left, ch, quit)
}
select {
case ch <- t.Value:
// vvvvvvvvvvvv
case <-quit:
return
}
// ^^^^^^^^^^^^
if t.Right != nil {
walk(t.Right, ch, quit)
}
}
Create a quit channel and pass it to each walker.
By closing quit
when the Same
exits, any running walkers are terminated.
func Same(t1, t2 *tree.Tree) bool {
// vvvvvvvvvvvv
quit := make(chan struct{})
defer close(quit)
w1, w2 := Walk(t1, quit), Walk(t2, quit)
// ^^^^^^^^^^^^
for {
v1, ok1 := <-w1
v2, ok2 := <-w2
if v1 != v2 || ok1 != ok2 {
return false
}
if !ok1 {
return true
}
}
}
Andrew adds:
Why not just kill the goroutines?
Goroutines are invisible to Go code. They can't be killed or waited on.
You have to build that yourself.
There's a reason:
As soon as Go code knows in which thread it runs you get thread-locality.
Thread-locality defeats the concurrency model.
- Channels are just values; they fit right into the type system.
- Goroutines are invisible to Go code; this gives you concurrency anywhere.
Less is more.