標籤:進度 struct ges 大小 loop ogre ret progress wait
練習 8.9: 編寫一個du工具,每隔一段時間將root目錄下的目錄大小計算並顯示出來。
package mainimport ( // "filepath" "flag" "fmt" "io/ioutil" "os" "path" "sync" "time")/*練習 8.9: 編寫一個du工具,每隔一段時間將root目錄下的目錄大小計算並顯示出來。*///接收命令列參數-vvar verbose = flag.Bool("v", false, "show verbose progress messages")func main() { //接收命令列參數,多個路徑 flag.Parse() roots := flag.Args() //如果沒傳遞任何路徑,給預設值 if len(roots) == 0 { roots = []string{"/"} } for { sumFileSize(roots) time.Sleep(20 * time.Second) } }func sumFileSize(roots []string) { //發送和接收檔案位元組大小的channel fileSizes := make(chan int64) //goroutine的計數器 var n sync.WaitGroup //迴圈命令列傳遞的路徑 for _, root := range roots { n.Add(1) //啟動goroutine計算 go walkDir(root, &n, fileSizes) } //啟動goroutine,等待所有計算目錄的goroutine結束 go func() { n.Wait() close(fileSizes) }() //定時顯示目錄進度發送的channel var tick <-chan time.Time if *verbose { tick = time.Tick(500 * time.Millisecond) } var nfiles, nbytes int64 //select和loop迴圈,多工loop: for { select { case size, ok := <-fileSizes: if !ok { break loop // fileSizes was closed } //計算目錄數,計算位元組大小 nfiles++ nbytes += size case <-tick: //接收到定時channel列印進度 printDiskUsage(nfiles, nbytes) } } //最後列印總計 printDiskUsage(nfiles, nbytes) // final totals}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 := path.Join(dir, entry.Name()) //開啟多個goroutine進行遞迴 go walkDir(subdir, n, fileSizes) } else { fileSizes <- entry.Size() } }}var sema = make(chan struct{}, 20)// dirents returns the entries of directory dir.func dirents(dir string) []os.FileInfo { //使用計數訊號量邏輯限制太多並發 sema <- struct{}{} entries, err := ioutil.ReadDir(dir) <-sema if err != nil { fmt.Fprintf(os.Stderr, "du1: %v\n", err) return nil } return entries}func printDiskUsage(nfiles, nbytes int64) { fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)}
[日常] Go語言聖經-樣本: 並發的目錄遍曆習題