关于关闭channel的疑惑

用这段代码测试关闭quit之后的反应

package main

import "fmt"

func test(quit chan int) {
	select {
		case <- quit:
		fmt.Println("pppppp")
		default:
		fmt.Println("qqqqqq")
	}
	fmt.Println("gggggg")
}

func test2(quit chan int) {
	select {
		case <- quit:
		fmt.Println("ttttttt") // 只执行了这句
		default:
		fmt.Println("qqqqqq")
	}
}

func main() {
	quit := make(chan int)
	close(quit)
	go test(quit)
	test2(quit)
}

打印结果如下:
ttttttt

为什么test函数中的打印没执行?
之前在stackoverflow上提问过一个递归问题,根本性质和这个差不多,其中有个回答说了这么一点:

When multiple goroutines are targeted with a cancellation signal, most often it is done by closing a channel, and not sending a value on the channel. Receiving from a closed channel can proceed immediately, no matter how many goroutines do that.
A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

还是不太明白。希望有大神能细致解释一下,非常感谢!

共 3 个回复


fising

主协程sleep 几秒试试呢?

# 0

BruceWangNo1

go test(quit)生成goroutine后,此goroutine被调度到runtime上的local runnable queue上,test2(quit)运行之后,main函数(G指向的runtime.main)所在的goroutine(G)结束了,进而程序退出了。go test(quit)对应的在local runnable queue里的goroutine没能来得及运行。

# 1

BruceWangNo1

以下代码可以解决你的问题。

package main

import "fmt"

func test(quit chan int, signal chan struct{}) {
	fmt.Println("program counter came here")
	select {
	case <-quit:
		fmt.Println("pppppp")
	default:
		fmt.Println("qqqqqq")
	}
	fmt.Println("gggggg")
	<-signal
}

func test2(quit chan int) {
	select {
	case <-quit:
		fmt.Println("ttttttt") // 只执行了这句
	default:
		fmt.Println("qqqqqq")
	}
}

func main() {
	quit := make(chan int)
	signal := make(chan struct{})
	close(quit)
	go test(quit, signal)
	test2(quit)
	signal <- struct{}{}
}
# 2