mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 19:54:21 +08:00
make loop
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
## 8.9. 併發的退齣
|
||||
## 8.9. 併發的退出
|
||||
|
||||
有時候我們需要通知goroutine停止它正在榦的事情,比如一個正在執行計算的web服務,然而它的客戶端已經斷開了和服務端的連接。
|
||||
|
||||
Go語言併沒有提供在一個goroutine中終止另一個goroutine的方法,由於這樣會導致goroutine之間的共享變量落在未定義的狀態上。在8.7節中的rocket launch程序中,我們往名字叫abort的channel里發送了一個簡單的值,在countdown的goroutine中會把這個值理解爲自己的退齣信號。但是如果我們想要退齣兩個或者任意多個goroutine怎麽辦呢?
|
||||
Go語言併沒有提供在一個goroutine中終止另一個goroutine的方法,由於這樣會導致goroutine之間的共享變量落在未定義的狀態上。在8.7節中的rocket launch程序中,我們往名字叫abort的channel里發送了一個簡單的值,在countdown的goroutine中會把這個值理解爲自己的退出信號。但是如果我們想要退出兩個或者任意多個goroutine怎麽辦呢?
|
||||
|
||||
一種可能的手段是向abort的channel里發送和goroutine數目一樣多的事件來退齣它們。如果這些goroutine中已經有一些自己退齣了,那麽會導致我們的channel里的事件數比goroutine還多,這樣導致我們的發送直接被阻塞。另一方面,如果這些goroutine又生成了其它的goroutine,我們的channel里的數目又太少了,所以有些goroutine可能會無法接收到退齣消息。一般情況下我們是很難知道在某一個時刻具體有多少個goroutine在運行着的。另外,當一個goroutine從abort channel中接收到一個值的時候,他會消費掉這個值,這樣其它的goroutine就沒法看到這條信息。爲了能夠達到我們退齣goroutine的目的,我們需要更靠譜的策略,來通過一個channel把消息廣播齣去,這樣goroutine們能夠看到這條事件消息,併且在事件完成之後,可以知道這件事已經發生過了。
|
||||
一種可能的手段是向abort的channel里發送和goroutine數目一樣多的事件來退出它們。如果這些goroutine中已經有一些自己退出了,那麽會導致我們的channel里的事件數比goroutine還多,這樣導致我們的發送直接被阻塞。另一方面,如果這些goroutine又生成了其它的goroutine,我們的channel里的數目又太少了,所以有些goroutine可能會無法接收到退出消息。一般情況下我們是很難知道在某一個時刻具體有多少個goroutine在運行着的。另外,當一個goroutine從abort channel中接收到一個值的時候,他會消費掉這個值,這樣其它的goroutine就沒法看到這條信息。爲了能夠達到我們退出goroutine的目的,我們需要更靠譜的策略,來通過一個channel把消息廣播出去,這樣goroutine們能夠看到這條事件消息,併且在事件完成之後,可以知道這件事已經發生過了。
|
||||
|
||||
迴憶一下我們關閉了一個channel併且被消費掉了所有已發送的值,操作channel之後的代碼可以立卽被執行,併且會産生零值。我們可以將這個機製擴展一下,來作爲我們的廣播機製:不要向channel發送值,而是用關閉一個channel來進行廣播。
|
||||
|
||||
隻要一些小脩改,我們就可以把退齣邏輯加入到前一節的du程序。首先,我們創建一個退齣的channel,這個channel不會向其中發送任何值,但其所在的閉包內要寫明程序需要退齣。我們同時還定義了一個工具函數,cancelled,這個函數在被調用的時候會輪詢退齣狀態。
|
||||
隻要一些小脩改,我們就可以把退出邏輯加入到前一節的du程序。首先,我們創建一個退出的channel,這個channel不會向其中發送任何值,但其所在的閉包內要寫明程序需要退出。我們同時還定義了一個工具函數,cancelled,這個函數在被調用的時候會輪詢退出狀態。
|
||||
|
||||
```go
|
||||
gopl.io/ch8/du4
|
||||
@@ -24,7 +24,7 @@ func cancelled() bool {
|
||||
}
|
||||
```
|
||||
|
||||
下面我們創建一個從標準輸入流中讀取內容的goroutine,這是一個比較典型的連接到終端的程序。每當有輸入被讀到(比如用戶按了迴車鍵),這個goroutine就會把取消消息通過關閉done的channel廣播齣去。
|
||||
下面我們創建一個從標準輸入流中讀取內容的goroutine,這是一個比較典型的連接到終端的程序。每當有輸入被讀到(比如用戶按了迴車鍵),這個goroutine就會把取消消息通過關閉done的channel廣播出去。
|
||||
|
||||
```go
|
||||
// Cancel traversal when input is detected.
|
||||
@@ -82,7 +82,7 @@ func dirents(dir string) []os.FileInfo {
|
||||
}
|
||||
```
|
||||
|
||||
現在當取消發生時,所有後颱的goroutine都會迅速停止併且主函數會返迴。當然,當主函數返迴時,一個程序會退齣,而我們又無法在主函數退齣的時候確認其已經釋放了所有的資源(譯註:因爲程序都退齣了,你的代碼都沒法執行了)。這里有一個方便的竅門我們可以一用:取代掉直接從主函數返迴,我們調用一個panic,然後runtime會把每一個goroutine的棧dump下來。如果main goroutine是唯一一個剩下的goroutine的話,他會清理掉自己的一切資源。但是如果還有其它的goroutine沒有退齣,他們可能沒辦法被正確地取消掉,也有可能被取消但是取消操作會很花時間;所以這里的一個調研還是很有必要的。我們用panic來穫取到足夠的信息來驗證我們上面的判斷,看看最終到底是什麽樣的情況。
|
||||
現在當取消發生時,所有後颱的goroutine都會迅速停止併且主函數會返迴。當然,當主函數返迴時,一個程序會退出,而我們又無法在主函數退出的時候確認其已經釋放了所有的資源(譯註:因爲程序都退出了,你的代碼都沒法執行了)。這里有一個方便的竅門我們可以一用:取代掉直接從主函數返迴,我們調用一個panic,然後runtime會把每一個goroutine的棧dump下來。如果main goroutine是唯一一個剩下的goroutine的話,他會清理掉自己的一切資源。但是如果還有其它的goroutine沒有退出,他們可能沒辦法被正確地取消掉,也有可能被取消但是取消操作會很花時間;所以這里的一個調研還是很有必要的。我們用panic來穫取到足夠的信息來驗證我們上面的判斷,看看最終到底是什麽樣的情況。
|
||||
|
||||
練習8.10: HTTP請求可能會因http.Request結構體中Cancel channel的關閉而取消。脩改8.6節中的web crawler來支持取消http請求。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user