golang中的select机制让我对大师的设计水平赞叹不已,它基于多个阻塞的通道操作(发送或接收),哪个可以执行,就执行哪个。

select {
case <- ch1:
	// ...
case x <- ch2:
	// use x ...
case ch3 <- y:
	// ...
}

前两个case是接收,第三个case是发送。golang的运行机制是,当前的goroutine会阻塞,当ch1或ch2可以接收事件时,或者ch3可以发送事件时,就触发对应的case代码块,然后select块结束。否则将一直阻塞。

select {}

如果不希望select阻塞,则可以设置一个default分支,在所有的case都阻塞地情况下,将会运行default分支。

示例1
func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}
fibonaccicquit
cfibonacciquitfibonacci
计时器

golang的计时器很适合和select语句搭配使用。

tick := time.Tick(time.Second)
tickchan time.Timetime.Tick()
ticktick
tick.Stop()
select {
case <-time.After(time.Second * 5):
	fmt.Println("bingo")
}
time.After

上面的代码将在5秒钟之后打印一句bingo。

计时器+select的示例

下面的代码来自《GO程序设计语言》。它实现的功能是,10秒钟之后将会触发一个事件(导弹发射),每秒钟打印一个数字,但是,如果中间用户敲击了一次键盘,则程序终止。

func main() {
	abort := make(chan struct{})
	go func() {
		os.Stdin.Read(make([]byte, 1))
		abort <- struct{}{}
	}()

	tick := time.Tick(time.Second)
	for countdown := 10; countdown >= 0; countdown-- {
		select {
		case <-tick:
			fmt.Println(countdown)
		case <-abort:
			fmt.Println("launch aborted!")
			return
		}
	}
	fmt.Println("launch!")
}
time.Tick
time.Sleep(time.Second)