This commit is contained in:
Xargin 2015-12-31 11:21:48 +08:00
parent 16900c13ca
commit f2d2a5e204

View File

@ -1,6 +1,7 @@
## 8.7. 基於select的多路複用 ## 8.7. 基於select的多路複用
下面的程序会进行火箭发射的倒计时。time.Tick函数返回一个channel程序会周期性地像一个节拍器一样向这个channel发送事件。每一个事件的值是一个时间戳 but it is rarely as interesting as the fact of its delivery.(这句实在不会翻译。。) 下面的程序会进行火箭发射的倒计时。time.Tick函数返回一个channel程序会周期性地像一个节拍器一样向这个channel发送事件。每一个事件的值是一个时间戳不过更有意思的是其传送方式。
```go ```go
gopl.io/ch8/countdown1 gopl.io/ch8/countdown1
func main() { func main() {
@ -40,10 +41,11 @@ default:
} }
``` ```
The general form of a select statement is shown above. Like a switch statement, it has a num- ber of cases and an optional default. Each case specifies a communication (a send or receive operation on some channel) and an associated block of statements. A receive expression may appear on its own, as in the first case, or within a short variable declaration, as in the second case; the second form lets you refer to the received value. 上面是select语句的一般形式。和switch语句稍微有点相似也会有几个case和最后的default选择支。每一个case代表一个通信操作(在某个channel上进行发送或者接收)并且会包含一些语句组成的一个语句块。一个接收表达式可能只包含接收表达式自身(译注:不把接收到的值赋值给变量什么的)就像上面的第一个case或者包含在一个简短的变量声明中像第二个case里一样第二种形式让你能够引用接收到的值。
A select waits until a communication for some case is ready to proceed. It then performs that communication and executes the cases associated statements; the other communications do not happen. A select with no cases, select{}, waits forever.
select会等待case中有能够执行的case时去执行。当条件满足时select才会去通信并执行case之后的语句 select会等待case中有能够执行的case时去执行。当条件满足时select才会去通信并执行case之后的语句这时候其它通信是不会执行的。一个没有任何case的select语句写作select{},会永远地等待下去。
Lets return to our rocket launch program. The time.After function immediately returns a channel, and starts a new goroutine that sends a single value on that channel after the speci- fied time. The select statement below waits until the first of two events arrives, either an abort event or the event indicating that 10 seconds have elapsed. If 10 seconds go by with no abort, the launch proceeds.
让我们回到我们的火箭发射程序。time.After函数会立即返回一个channel并起一个新的goroutine在经过特定的时间后向该channel发送一个独立的值。下面的select语句会会一直等待到两个事件中的一个到达无论是abort事件或者一个10秒经过的事件。如果10秒经过了还没有abort事件进入那么火箭就会发射。
```go ```go
@ -59,6 +61,7 @@ func main() {
return return
} }
launch() launch()
}
``` ```
@ -76,6 +79,7 @@ for i := 0; i < 10; i++ {
``` ```
如果多个case同时就绪时select会随机地选择一个执行这样来保证每一个channel都有平等的被select的机会。增加前一个例子的buffer大小会使其输出变得不确定因为当buffer既不为满也不为空时select语句的执行情况就像是抛硬币的行为一样是随机的。 如果多个case同时就绪时select会随机地选择一个执行这样来保证每一个channel都有平等的被select的机会。增加前一个例子的buffer大小会使其输出变得不确定因为当buffer既不为满也不为空时select语句的执行情况就像是抛硬币的行为一样是随机的。
下面让我们的发射程序打印倒计时。这里的select语句会使每次循环迭代等待一秒来执行退出操作。 下面让我们的发射程序打印倒计时。这里的select语句会使每次循环迭代等待一秒来执行退出操作。
```go ```go
@ -99,7 +103,8 @@ func main() {
} }
``` ```
The time.Tick function behaves as if it creates a goroutine that calls time.Sleep in a loop, sending an event each time it wakes up. When the countdown function above returns, it stops receiving events from tick, but the ticker goroutine is still there, trying in vain to send on a channel from which no goroutine is receiving—a goroutine leak (§8.4.4). time.Tick函数表现得好像它创建了一个在循环中调用time.Sleep的goroutine每次被唤醒时发送一个事件。当countdown函数返回时它会停止从tick中接收事件但是ticker这个goroutine还依然存活继续徒劳地尝试从channel中发送值然而这时候已经没有其它的goroutine会从该channel中接收值了--这被称为goroutine泄露(§8.4.4)。
Tick函数挺方便但是只有当程序整个生命周期都需要这个时间时我们使用它才比较合适。否则的话我们应该使用下面的这种模式 Tick函数挺方便但是只有当程序整个生命周期都需要这个时间时我们使用它才比较合适。否则的话我们应该使用下面的这种模式
```go ```go
@ -109,6 +114,7 @@ ticker.Stop() // cause the ticker's goroutine to terminate
``` ```
有时候我们希望能够从channel中发送或者接收值并避免因为发送或者接收导致的阻塞尤其是当channel没有准备好写或者读时。select语句就可以实现这样的功能。select会有一个default来设置当其它的操作都不能够马上被处理时程序需要执行哪些逻辑。 有时候我们希望能够从channel中发送或者接收值并避免因为发送或者接收导致的阻塞尤其是当channel没有准备好写或者读时。select语句就可以实现这样的功能。select会有一个default来设置当其它的操作都不能够马上被处理时程序需要执行哪些逻辑。
下面的select语句会在abort channel中有值时从其中接收值无值时什么都不做。这是一个非阻塞的接收操作反复地做这样的操作叫做“轮询channel”。 下面的select语句会在abort channel中有值时从其中接收值无值时什么都不做。这是一个非阻塞的接收操作反复地做这样的操作叫做“轮询channel”。
```go ```go
@ -121,5 +127,7 @@ default:
} }
``` ```
channel的零值是nil。也许会让你觉得比较奇怪nil的channel有时候也是有一些用处的。因为对一个nil的channel发送和接收操作会永远阻塞在select语句中操作nil的channel永远都不会被select到。 channel的零值是nil。也许会让你觉得比较奇怪nil的channel有时候也是有一些用处的。因为对一个nil的channel发送和接收操作会永远阻塞在select语句中操作nil的channel永远都不会被select到。
This lets us use nil to enable or disable cases that cor- respond to features like handling timeouts or cancellation, responding to other input events, or emitting output. Well see an example in the next section.
这使得我们可以用nil来激活或者禁用case来达成处理其它输入或输出事件时超时和取消的逻辑。我们会在下一节中看到一个例子。
练习8.8: 使用select来改造8.3节中的echo服务器为其增加超时这样服务器可以在客户端10秒中没有任何喊话时自动断开连接。 练习8.8: 使用select来改造8.3节中的echo服务器为其增加超时这样服务器可以在客户端10秒中没有任何喊话时自动断开连接。