Fixes #198
This commit is contained in:
chai2010
2016-01-18 11:22:04 +08:00
parent 884ada9cd0
commit 9666211cd7
71 changed files with 107 additions and 105 deletions

View File

@@ -112,7 +112,7 @@ $ killall clock1
killall命令是一個Unix命令行工具可以用給定的進程名來殺掉所有名字匹配的進程。
第二個客戶端必等待第一個客戶端完成工作這樣服務端才能繼續向後執行因爲我們這里的服務器程序同一時間隻能處理一個客戶端連接。我們這里對服務端程序做一點小改動使其支持併發在handleConn函數調用的地方增加go關鍵字讓每一次handleConn的調用都進入一個獨立的goroutine。
第二個客戶端必等待第一個客戶端完成工作這樣服務端才能繼續向後執行因爲我們這里的服務器程序同一時間隻能處理一個客戶端連接。我們這里對服務端程序做一點小改動使其支持併發在handleConn函數調用的地方增加go關鍵字讓每一次handleConn的調用都進入一個獨立的goroutine。
```go
gopl.io/ch8/clock2

View File

@@ -72,7 +72,7 @@ func request(hostname string) (response string) { /* ... */ }
關於無緩存或帶緩存channels之間的選擇或者是帶緩存channels的容量大小的選擇都可能影響程序的正確性。無緩存channel更強地保證了每個發送操作與相應的同步接收操作但是對於帶緩存channel這些操作是解耦的。同樣卽使我們知道將要發送到一個channel的信息的數量上限創建一個對應容量大小帶緩存channel也是不現實的因爲這要求在執行任何接收操作之前緩存所有已經發送的值。如果未能分配足夠的緩衝將導致程序死鎖。
Channel的緩存也可能影響程序的性能。想象一家蛋糕店有三個廚師一個烘焙一個上衣,還有一個將每個蛋糕傳遞到它下一個廚師在生産線。在狹小的廚房空間環境,每個廚師在完成蛋糕後必等待下一個廚師已經準備好接受它這類似於在一個無緩存的channel上進行溝通。
Channel的緩存也可能影響程序的性能。想象一家蛋糕店有三個廚師一個烘焙一個上衣,還有一個將每個蛋糕傳遞到它下一個廚師在生産線。在狹小的廚房空間環境,每個廚師在完成蛋糕後必等待下一個廚師已經準備好接受它這類似於在一個無緩存的channel上進行溝通。
如果在每個廚師之間有一個放置一個蛋糕的額外空間那麽每個廚師就可以將一個完成的蛋糕臨時放在那里而馬上進入下一個蛋糕在製作中這類似於將channel的緩存隊列的容量設置爲1。隻要每個廚師的平均工作效率相近那麽其中大部分的傳輸工作將是迅速的個體之間細小的效率差異將在交接過程中瀰補。如果廚師之間有更大的額外空間——也是就更大容量的緩存隊列——將可以在不停止生産線的前提下消除更大的效率波動例如一個廚師可以短暫地休息然後在加快趕上進度而不影響其其他人。

View File

@@ -176,9 +176,9 @@ func makeThumbnails6(filenames <-chan string) int64 {
}
```
註意Add和Done方法的不對策。Add是爲計數器加一在worker goroutine開始之前調用而不是在goroutine中否則的話我們沒辦法確定Add是在"closer" goroutine調用Wait之前被調用。併且Add還有一個參數但Done卻沒有任何參數其實它和Add(-1)是等價的。我們使用defer來確保計數器卽使是在出錯的情況下依然能夠正確地被減掉。上面的程序代碼結構是當我們使用併發循環但又不知道迭代次數時很通常而且很地道的寫法。
註意Add和Done方法的不對策。Add是爲計數器加一在worker goroutine開始之前調用而不是在goroutine中否則的話我們沒辦法確定Add是在"closer" goroutine調用Wait之前被調用。併且Add還有一個參數但Done卻沒有任何參數其實它和Add(-1)是等價的。我們使用defer來確保計數器卽使是在出錯的情況下依然能夠正確地被減掉。上面的程序代碼結構是當我們使用併發循環但又不知道迭代次數時很通常而且很地道的寫法。
sizes channel攜帶了每一個文件的大小到main goroutine在main goroutine中使用了range loop來計算總和。觀察一下我們是怎樣創建一個closer goroutine併讓其等待worker們在關閉掉sizes channel之前退出的。兩步操作wait和close是基於sizes的循環的併發。考慮一下另一種方案如果等待操作被放在了main goroutine中在循環之前這樣的話就永遠都不會結束了如果在循環之後那麽又變成了不可達的部分因爲沒有任何東西去關閉這個channel這個循環就永遠都不會終止。
sizes channel攜帶了每一個文件的大小到main goroutine在main goroutine中使用了range loop來計算總和。觀察一下我們是怎樣創建一個closer goroutine併讓其等待worker們在關閉掉sizes channel之前退出的。兩步操作wait和close是基於sizes的循環的併發。考慮一下另一種方案如果等待操作被放在了main goroutine中在循環之前這樣的話就永遠都不會結束了如果在循環之後那麽又變成了不可達的部分因爲沒有任何東西去關閉這個channel這個循環就永遠都不會終止。
圖8.5 表明了makethumbnails6函數中事件的序列。縱列表示goroutine。窄線段代表sleep粗線段代表活動。斜線箭頭代表用來同步兩個goroutine的事件。時間向下流動。註意main goroutine是如何大部分的時間被喚醒執行其range循環等待worker發送值或者closer來關閉channel的。

View File

@@ -116,7 +116,7 @@ loop:
printDiskUsage(nfiles, nbytes) // final totals
}
```
由於我們的程序不再使用range循環第一個select的case必顯式地判斷fileSizes的channel是不是已經被關閉了這里可以用到channel接收的二值形式。如果channel已經被關閉了的話程序會直接退出循環。這里的break語句用到了標籤break這樣可以同時終結select和for兩個循環如果沒有用標籤就break的話隻會退出內層的select循環而外層的for循環會使之進入下一輪select循環。
由於我們的程序不再使用range循環第一個select的case必顯式地判斷fileSizes的channel是不是已經被關閉了這里可以用到channel接收的二值形式。如果channel已經被關閉了的話程序會直接退出循環。這里的break語句用到了標籤break這樣可以同時終結select和for兩個循環如果沒有用標籤就break的話隻會退出內層的select循環而外層的for循環會使之進入下一輪select循環。
現在程序會悠閒地爲我們打印更新流: