mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-01 02:28:56 +00:00
make loop
This commit is contained in:
parent
191ac2e218
commit
32b21df6ad
@ -18,7 +18,7 @@
|
||||
|
||||
中文譯者 | 章節
|
||||
-------------------------------------- | -------------------------
|
||||
`chai2010 <chaishushan@gmail.com>` | 前言/第二章/第三章/第十章/第十一章/第十二章/第十三章
|
||||
`chai2010 <chaishushan@gmail.com>` | 前言/第二章/第三章/第四章/第十章/第十一章/第十二章/第十三章
|
||||
`CrazySssst` | 第三章(0節)
|
||||
`foreversmart <njutree@gmail.com>` | 第七章(0節和1節)
|
||||
`Xargin <cao1988228@163.com>` | 第一章/第六章/第八章
|
||||
|
@ -1,6 +1,6 @@
|
||||
## 8.7. 基於select的多路複用
|
||||
|
||||
下面的程序会进行火箭发射的倒计时。time.Tick函数返回一个channel,程序会周期性地像一个节拍器一样向这个channel发送事件。每一个事件的值是一个时间戳,不过更有意思的是其传送方式。
|
||||
下面的程序會進行火箭發射的倒計時。time.Tick函數返迴一個channel,程序會週期性地像一個節拍器一樣向這個channel發送事件。每一個事件的值是一個時間戳,不過更有意思的是其傳送方式。
|
||||
|
||||
```go
|
||||
gopl.io/ch8/countdown1
|
||||
@ -15,7 +15,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
现在我们让这个程序支持在倒计时中,用户按下return键时直接中断发射流程。首先,我们启动一个goroutine,这个goroutine会尝试从标准输入中调入一个单独的byte并且,如果成功了,会向名为abort的channel发送一个值。
|
||||
現在我們讓這個程序支持在倒計時中,用戶按下return鍵時直接中斷發射流程。首先,我們啟動一個goroutine,這個goroutine會嚐試從標準輸入中調入一個單獨的byte併且,如果成功了,會向名爲abort的channel發送一個值。
|
||||
|
||||
```go
|
||||
gopl.io/ch8/countdown2
|
||||
@ -26,7 +26,7 @@ go func() {
|
||||
}()
|
||||
```
|
||||
|
||||
现在每一次计数循环的迭代都需要等待两个channel中的其中一个返回事件了:ticker channel当一切正常时(就像NASA jorgon的"nominal",译注:这梗估计我们是不懂了)或者异常时返回的abort事件。我们无法做到从每一个channel中接收信息,如果我们这么做的话,如果第一个channel中没有事件发过来那么程序就会立刻被阻塞,这样我们就无法收到第二个channel中发过来的事件。这时候我们需要多路复用(multiplex)这些操作了,为了能够多路复用,我们使用了select语句。
|
||||
現在每一次計數循環的迭代都需要等待兩個channel中的其中一個返迴事件了:ticker channel當一切正常時(就像NASA jorgon的"nominal",譯註:這梗估計我們是不懂了)或者異常時返迴的abort事件。我們無法做到從每一個channel中接收信息,如果我們這麽做的話,如果第一個channel中沒有事件發過來那麽程序就會立刻被阻塞,這樣我們就無法收到第二個channel中發過來的事件。這時候我們需要多路複用(multiplex)這些操作了,爲了能夠多路複用,我們使用了select語句。
|
||||
|
||||
```go
|
||||
select {
|
||||
@ -41,11 +41,11 @@ default:
|
||||
}
|
||||
```
|
||||
|
||||
上面是select语句的一般形式。和switch语句稍微有点相似,也会有几个case和最后的default选择支。每一个case代表一个通信操作(在某个channel上进行发送或者接收)并且会包含一些语句组成的一个语句块。一个接收表达式可能只包含接收表达式自身(译注:不把接收到的值赋值给变量什么的),就像上面的第一个case,或者包含在一个简短的变量声明中,像第二个case里一样;第二种形式让你能够引用接收到的值。
|
||||
上面是select語句的一般形式。和switch語句稍微有點相似,也會有幾個case和最後的default選擇支。每一個case代表一個通信操作(在某個channel上進行發送或者接收)併且會包含一些語句組成的一個語句塊。一個接收表達式可能隻包含接收表達式自身(譯註:不把接收到的值賦值給變量什麽的),就像上面的第一個case,或者包含在一個簡短的變量聲明中,像第二個case里一樣;第二種形式讓你能夠引用接收到的值。
|
||||
|
||||
select会等待case中有能够执行的case时去执行。当条件满足时,select才会去通信并执行case之后的语句;这时候其它通信是不会执行的。一个没有任何case的select语句写作select{},会永远地等待下去。
|
||||
select會等待case中有能夠執行的case時去執行。當條件滿足時,select才會去通信併執行case之後的語句;這時候其它通信是不會執行的。一個沒有任何case的select語句寫作select{},會永遠地等待下去。
|
||||
|
||||
让我们回到我们的火箭发射程序。time.After函数会立即返回一个channel,并起一个新的goroutine在经过特定的时间后向该channel发送一个独立的值。下面的select语句会会一直等待到两个事件中的一个到达,无论是abort事件或者一个10秒经过的事件。如果10秒经过了还没有abort事件进入,那么火箭就会发射。
|
||||
讓我們迴到我們的火箭發射程序。time.After函數會立卽返迴一個channel,併起一個新的goroutine在經過特定的時間後向該channel發送一個獨立的值。下面的select語句會會一直等待到兩個事件中的一個到達,無論是abort事件或者一個10秒經過的事件。如果10秒經過了還沒有abort事件進入,那麽火箭就會發射。
|
||||
|
||||
|
||||
```go
|
||||
@ -65,7 +65,7 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
下面这个例子更微秒。ch这个channel的buffer大小是1,所以会交替的为空或为满,所以只有一个case可以进行下去,无论i是奇数或者偶数,它都会打印0 2 4 6 8。
|
||||
下面這個例子更微秒。ch這個channel的buffer大小是1,所以會交替的爲空或爲滿,所以隻有一個case可以進行下去,無論i是奇數或者偶數,它都會打印0 2 4 6 8。
|
||||
|
||||
```go
|
||||
ch := make(chan int, 1)
|
||||
@ -78,9 +78,9 @@ for i := 0; i < 10; i++ {
|
||||
}
|
||||
```
|
||||
|
||||
如果多个case同时就绪时,select会随机地选择一个执行,这样来保证每一个channel都有平等的被select的机会。增加前一个例子的buffer大小会使其输出变得不确定,因为当buffer既不为满也不为空时,select语句的执行情况就像是抛硬币的行为一样是随机的。
|
||||
如果多個case同時就緒時,select會隨機地選擇一個執行,這樣來保證每一個channel都有平等的被select的機會。增加前一個例子的buffer大小會使其輸出變得不確定,因爲當buffer旣不爲滿也不爲空時,select語句的執行情況就像是拋硬幣的行爲一樣是隨機的。
|
||||
|
||||
下面让我们的发射程序打印倒计时。这里的select语句会使每次循环迭代等待一秒来执行退出操作。
|
||||
下面讓我們的發射程序打印倒計時。這里的select語句會使每次循環迭代等待一秒來執行退出操作。
|
||||
|
||||
```go
|
||||
gopl.io/ch8/countdown3
|
||||
@ -103,9 +103,9 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
time.Tick函数表现得好像它创建了一个在循环中调用time.Sleep的goroutine,每次被唤醒时发送一个事件。当countdown函数返回时,它会停止从tick中接收事件,但是ticker这个goroutine还依然存活,继续徒劳地尝试从channel中发送值,然而这时候已经没有其它的goroutine会从该channel中接收值了--这被称为goroutine泄露(§8.4.4)。
|
||||
time.Tick函數表現得好像它創建了一個在循環中調用time.Sleep的goroutine,每次被喚醒時發送一個事件。當countdown函數返迴時,它會停止從tick中接收事件,但是ticker這個goroutine還依然存活,繼續徒勞地嚐試從channel中發送值,然而這時候已經沒有其它的goroutine會從該channel中接收值了--這被稱爲goroutine洩露(§8.4.4)。
|
||||
|
||||
Tick函数挺方便,但是只有当程序整个生命周期都需要这个时间时我们使用它才比较合适。否则的话,我们应该使用下面的这种模式:
|
||||
Tick函數挺方便,但是隻有當程序整個生命週期都需要這個時間時我們使用它才比較合適。否則的話,我們應該使用下面的這種模式:
|
||||
|
||||
```go
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
@ -113,9 +113,9 @@ ticker := time.NewTicker(1 * time.Second)
|
||||
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
|
||||
select {
|
||||
@ -126,8 +126,8 @@ default:
|
||||
// do nothing
|
||||
}
|
||||
```
|
||||
channel的零值是nil。也许会让你觉得比较奇怪,nil的channel有时候也是有一些用处的。因为对一个nil的channel发送和接收操作会永远阻塞,在select语句中操作nil的channel永远都不会被select到。
|
||||
channel的零值是nil。也許會讓你覺得比較奇怪,nil的channel有時候也是有一些用處的。因爲對一個nil的channel發送和接收操作會永遠阻塞,在select語句中操作nil的channel永遠都不會被select到。
|
||||
|
||||
这使得我们可以用nil来激活或者禁用case,来达成处理其它输入或输出事件时超时和取消的逻辑。我们会在下一节中看到一个例子。
|
||||
這使得我們可以用nil來激活或者禁用case,來達成處理其它輸入或輸出事件時超時和取消的邏輯。我們會在下一節中看到一個例子。
|
||||
|
||||
练习8.8: 使用select来改造8.3节中的echo服务器,为其增加超时,这样服务器可以在客户端10秒中没有任何喊话时自动断开连接。
|
||||
練習8.8: 使用select來改造8.3節中的echo服務器,爲其增加超時,這樣服務器可以在客戶端10秒中沒有任何喊話時自動斷開連接。
|
||||
|
Loading…
Reference in New Issue
Block a user