原文地址:https://blog.golang.org/concurrency-timeouts
并发变成有它自己的风格. 一个非常好的例子就是 timeout. 虽然 go 的 channel 没有直接支持 timeout 机制,但是要实现它非常容易.
比如说,我们想从一个 channel ch 中接收数据,但是最多只想等待 1 秒. 我们可以这么做:创建一个 channel 作为信号 channel (signalling channel), 再创建一个 goroutine,这个 goroutine 再发送数据之前先休眠 1 秒.
timeout := make(chan bool, 1)
go func() {time.Sleep(1 * time.Second)timeout <- true
}()
然后,我们使用 select
语句来从 ch 上接受数据. 如果在 1 秒之后,ch 上没有数据到来,那么我们将会进入timeout 分支.
select {
case <-ch:// a read from ch has occurred
case <-timeout:// the read from ch has timed out
}
timeout channel 的 buffer 大小为 1, 当 goroutine 发送了 timeout 信号之后,便退出了. goroutine 并不知道它发送的超时信号是否已经被接受,这意味着,如果 在 1秒之内,成功的从 ch 上收到了数据, goroutine 不会因为等待它发送到 timeout channel 上的信号被接受而永远阻塞.
接下来,我们来看一下这个模式的一种变体. 在这个例子中,我们的程序从多个数据库中同时读取数据. 它仅仅需要一个结果,因此它只会接收最先到达的数据.
Query
输入一个数据库连接的集合和一个查询字符串,它会同时查询这些数据库,接收第一个查询结果.
func Query(conns []Conn, query string) Result {ch := make(chan Result)for _, conn := range conns {go func(c Conn) {select {case ch <- c.DoQuery(query):default:}}(conn)}return <-ch
}
这个方法中,我们为每个数据库查询操作开启一个 goroutine,在 goroutine 中以非阻塞模式返送请求,等待查询结果. 为每个数据库连接使用一个单独 goroutine 确保了一个操作不会影响另一个操作,这些操作并行进行.
END!!!