ch8: fix code format

This commit is contained in:
chai2010
2016-01-21 10:39:06 +08:00
parent 0b5ec941ed
commit b1730821fe
13 changed files with 393 additions and 404 deletions

View File

@@ -2,8 +2,8 @@
在本小節中我們會創建一個程序來生成指定目録的硬盤使用情況報告這個程序和Unix里的du工具比較相似。大多數工作用下面這個walkDir函數來完成這個函數使用dirents函數來枚舉一個目録下的所有入口。
<u><i>gopl.io/ch8/du1</i></u>
```go
gopl.io/ch8/du1
// walkDir recursively walks the file tree rooted at dir
// and sends the size of each found file on fileSizes.
func walkDir(dir string, fileSizes chan<- int64) {
@@ -70,9 +70,8 @@ func main() {
}
func printDiskUsage(nfiles, nbytes int64) {
fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)
fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)
}
```
這個程序會在打印其結果之前卡住很長時間。
@@ -87,8 +86,8 @@ $ ./du1 $HOME /usr /bin /etc
下面這個du的變種會間歇打印內容不過隻有在調用時提供了-v的flag才會顯示程序進度信息。在roots目録上循環的後台goroutine在這里保持不變。主goroutine現在使用了計時器來每500ms生成事件然後用select語句來等待文件大小的消息來更新總大小數據或者一個計時器的事件來打印當前的總大小數據。如果-v的flag在運行時沒有傳入的話tick這個channel會保持爲nil這樣在select里的case也就相當於被禁用了。
<u><i>gopl.io/ch8/du2</i></u>
```go
gopl.io/ch8/du2
var verbose = flag.Bool("v", false, "show verbose progress messages")
func main() {
@@ -116,6 +115,7 @@ loop:
printDiskUsage(nfiles, nbytes) // final totals
}
```
由於我們的程序不再使用range循環第一個select的case必須顯式地判斷fileSizes的channel是不是已經被關閉了這里可以用到channel接收的二值形式。如果channel已經被關閉了的話程序會直接退出循環。這里的break語句用到了標籤break這樣可以同時終結select和for兩個循環如果沒有用標籤就break的話隻會退出內層的select循環而外層的for循環會使之進入下一輪select循環。
現在程序會悠閒地爲我們打印更新流:
@@ -133,50 +133,49 @@ $ ./du2 -v $HOME /usr /bin /etc
然而這個程序還是會花上很長時間才會結束。無法對walkDir做併行化處理沒什麽别的原因無非是因爲磁盤繫統併行限製。下面這個第三個版本的du會對每一個walkDir的調用創建一個新的goroutine。它使用sync.WaitGroup (§8.5)來對仍舊活躍的walkDir調用進行計數另一個goroutine會在計數器減爲零的時候將fileSizes這個channel關閉。
<u><i>gopl.io/ch8/du3</i></u>
```go
gopl.io/ch8/du3
func main() {
// ...determine roots...
// Traverse each root of the file tree in parallel.
fileSizes := make(chan int64)
var n sync.WaitGroup
for _, root := range roots {
n.Add(1)
go walkDir(root, &n, fileSizes)
}
go func() {
n.Wait()
close(fileSizes)
}()
// ...select loop...
// ...determine roots...
// Traverse each root of the file tree in parallel.
fileSizes := make(chan int64)
var n sync.WaitGroup
for _, root := range roots {
n.Add(1)
go walkDir(root, &n, fileSizes)
}
go func() {
n.Wait()
close(fileSizes)
}()
// ...select loop...
}
func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64) {
defer n.Done()
for _, entry := range dirents(dir) {
if entry.IsDir() {
n.Add(1)
subdir := filepath.Join(dir, entry.Name())
go walkDir(subdir, n, fileSizes)
} else {
fileSizes <- entry.Size()
}
}
defer n.Done()
for _, entry := range dirents(dir) {
if entry.IsDir() {
n.Add(1)
subdir := filepath.Join(dir, entry.Name())
go walkDir(subdir, n, fileSizes)
} else {
fileSizes <- entry.Size()
}
}
}
```
由於這個程序在高峯期會創建成百上韆的goroutine我們需要脩改dirents函數用計數信號量來阻止他同時打開太多的文件就像我們在8.7節中的併發爬蟲一樣:
```go
// sema is a counting semaphore for limiting concurrency in dirents.
var sema = make(chan struct{}, 20)
// dirents returns the entries of directory dir.
func dirents(dir string) []os.FileInfo {
sema <- struct{}{} // acquire token
defer func() { <-sema }() // release token
// ...
sema <- struct{}{} // acquire token
defer func() { <-sema }() // release token
// ...
```
這個版本比之前那個快了好幾倍,盡管其具體效率還是和你的運行環境,機器配置相關。