Closing 2 channels in one Go routine causes deadlock?

Hi, I am playing with Tour of Go exercise Equivalent Binary Trees. I have completed the code but I notice something strange:

package main

import "golang.org/x/tour/tree"

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan<- int) {
	if t == nil {
		return
	}
	Walk(t.Left, ch) 
	ch <- t.Value
	Walk(t.Right, ch) 
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
	c1 := make(chan int)
	c2 := make(chan int)

	go func() {
		Walk(t1, c1)
		close(c1)
	}() 
	go func() {
		Walk(t2, c2)
		close(c2)
	}() 
	
	for i := range c1 {
		if i != <-c2 {
			return false
		} 
	}
	
	return true
}

func main() {
	println(Same(tree.New(10), tree.New(10)))
}

As can be seen, in the Same method, if I use the following equivalent code instead, then deadlock will arise. Can someone help me explain this problem? Thanks

	go func() {
		Walk(t1, c1)
		close(c1)
		Walk(t2, c2)
		close(c2)
	}()
  1. Your “walker goroutine” starts
  2. It enters Walk(t1, c1)
  3. Walk(t1, c1) sends its first value into c1
  4. Your main goroutine receives the first value from c1 in the for … range loop
  5. Your main goroutine attempts a receive from c2 to compare it to the value from c1.
  6. Your “walker goroutine” hasn’t returned from Walk(t1, c1) yet, so Walk(t2, c2) hasn’t started. Nothing is getting sent into c2, so receiving from it will deadlock.
1 Like

main and go1 all blocked.
main need recv ch1 and ch2, but not recv ch2. go1 not send value to c2 in block state, so main revc c2 in block state,all goroutine to blocked state.

  • go1 send c1-10 succes
  • main c1 recv c1-10 succes
  • main wait c2 recv
  • go1 send c2-20 block

https://play.golang.org/p/Ji-ysAFdISI


g1 need c1 send 10ci value 10-100, then c2 send 10ci.
main need recv c1 then recv c2 Repeat 10ci.
but g1 send ‘20’ main is blocked state wait revc c2,so g1 and main in blocked, if c1 and c2 cap is 10,then not blocked.

Thanks. I somehow get it. But I have one more question: Why “send 20” must wait for range to pop it out? I mean, from my point of view, it is in a different thread, so why can’t we just put to channel independently?

Feels like there is a mutex or something c1.

Thanks

Thank you, I somehow get it.

it is in a different goroutine, Chan is essentially a blocking queue with a lock.
chan accepts a value that is blocked if there is no data, and sends a value that is also blocked if chan is full.