make loop

This commit is contained in:
chai2010
2015-12-18 14:49:31 +08:00
parent 9fde1ff772
commit f9ac065e47
106 changed files with 725 additions and 725 deletions

View File

@@ -1,12 +1,12 @@
## 8.9. 發的退齣
## 8.9. 發的退齣
有時候我們需要通知goroutine停止它正在的事情比如一個正在執行計算的web服務然而它的客戶端已經斷開了和服務端的連接。
有時候我們需要通知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來進行廣播。
迴憶一下我們關閉了一個channel且被消費掉了所有已發送的值操作channel之後的代碼可以立卽被執行且會生零值。我們可以將這個機製擴展一下來作爲我們的廣播機製不要向channel發送值而是用關閉一個channel來進行廣播。
隻要一些小脩改我們就可以把退齣邏輯加入到前一節的du程序。首先我們創建一個退齣的channel這個channel不會向其中發送任何值但其所在的閉包內要寫明程序需要退齣。我們同時還定義了一個工具函數cancelled這個函數在被調用的時候會輪詢退齣狀態。
@@ -34,7 +34,7 @@ go func() {
}()
```
現在我們需要使我們的goroutine來對取消進行響應。在main goroutine中我們添加了select的第三個case語句試從done channel中接收內容。如果這個case被滿足的話在select到的時候卽會返迴但在結束之前我們需要把fileSizes channel中的內容“排”空在channel被關閉之前棄掉所有值。這樣可以保對walkDir的調用不要被向fileSizes發送信息阻塞住可以正確地完成。
現在我們需要使我們的goroutine來對取消進行響應。在main goroutine中我們添加了select的第三個case語句試從done channel中接收內容。如果這個case被滿足的話在select到的時候卽會返迴但在結束之前我們需要把fileSizes channel中的內容“排”空在channel被關閉之前棄掉所有值。這樣可以保對walkDir的調用不要被向fileSizes發送信息阻塞住可以正確地完成。
```go
for {
@@ -51,7 +51,7 @@ for {
}
```
walkDir這個goroutine一啟動就會輪詢取消狀態如果取消狀態被設置的話會直接返迴且不做額外的事情。這樣我們將所有在取消事件之後創建的goroutine改變爲無操作。
walkDir這個goroutine一啟動就會輪詢取消狀態如果取消狀態被設置的話會直接返迴且不做額外的事情。這樣我們將所有在取消事件之後創建的goroutine改變爲無操作。
```go
func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64) {
@@ -66,9 +66,9 @@ func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64) {
```
在walkDir函數的循環中我們對取消狀態進行輪詢可以帶來明顯的益處可以避免在取消事件發生時還去創建goroutine。取消本身是有一些代價的想要快速的響應需要對程序邏輯進行侵入式的脩改。確保在取消發生之後不要有代價太大的操作可能會需要脩改你代碼的很多地方,但是在一些重要的地方去檢査取消事件也確實能帶來很大的好處。
在walkDir函數的循環中我們對取消狀態進行輪詢可以帶來明顯的益處可以避免在取消事件發生時還去創建goroutine。取消本身是有一些代價的想要快速的響應需要對程序邏輯進行侵入式的脩改。確保在取消發生之後不要有代價太大的操作可能會需要脩改你代碼的很多地方,但是在一些重要的地方去檢査取消事件也確實能帶來很大的好處。
對這個程序的一個簡單的性能分析可以揭示瓶頸在dirents函數中取一個信號量。下面的select可以讓這種操作可以被取消且可以將取消時的延遲從幾百毫秒降低到幾十毫秒。
對這個程序的一個簡單的性能分析可以揭示瓶頸在dirents函數中取一個信號量。下面的select可以讓這種操作可以被取消且可以將取消時的延遲從幾百毫秒降低到幾十毫秒。
```go
func dirents(dir string) []os.FileInfo {
@@ -82,10 +82,10 @@ 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請求。
提示: http.Get沒有提供方便地定製一個請求的方法。你可以用http.NewRequest來取而代之設置它的Cancel字段然後用http.DefaultClient.Do(req)來進行這個http請求。
提示: http.Get沒有提供方便地定製一個請求的方法。你可以用http.NewRequest來取而代之設置它的Cancel字段然後用http.DefaultClient.Do(req)來進行這個http請求。
練習8.11:緊接着8.4.4中的mirroredQuery流程實現一個發請求url的fetch的變種。當第一個請求返迴時直接取消其它的請求。
練習8.11:緊接着8.4.4中的mirroredQuery流程實現一個發請求url的fetch的變種。當第一個請求返迴時直接取消其它的請求。