package goConcurrency // 并发遍历目录统计信息的Demo import ( "fmt" "io/fs" "log" "os" "path/filepath" "sync" ) // WalkDir 外部调用的遍历目录统计信息的方法 func WalkDir(dirs ...string) string { // 一:保证至少有一个目录需要统计遍历 // 默认为当前目录 if len(dirs) == 0 { dirs = []string{"."} } // 二:初始化变量,channel用于完成Size的传递,WaitGroup用于等待调度 filesizeCh := make(chan int64, 1) wg := &sync.WaitGroup{} // 三:启动多个Goroutine统计信息,取决于len(dirs) for _, dir := range dirs { wg.Add(1) // +1 // 并发的遍历统计每个目录的信息 go walkDir(dir, filesizeCh, wg) } // 四:启动累计运算的Goroutine // 1.用户关闭filesizeCh go func(filesizeCh chan int64, wg *sync.WaitGroup) { //等待统计工作完成 wg.Wait() // 关闭filesizeCh close(filesizeCh) }(filesizeCh, wg) // 2.range的方式从filesizeCh中获取文件大小 // 3.将统计结果,使用channel传递出来 fileNumCh := make(chan int64, 1) sizeTotalCh := make(chan int64, 1) go func(filesizeCh <-chan int64, fileNumCh, sizeTotalCh chan<- int64) { // 统计文件数,和文件整体大小 var fileNum, sizeTotal int64 // 遍历全部的filesizeCh元素,统计文件数量和大小,直到channel被关闭 for filesize := range filesizeCh { // 累计文件数,和统计文件整体大小 fileNum++ sizeTotal += filesize } // 将统计结果发送到对于的Channel中 fileNumCh <- fileNum sizeTotalCh <- sizeTotal }(filesizeCh, fileNumCh, sizeTotalCh) // 五:整理返回值 // size 的单位 Byte // 需要的单位 MB result := fmt.Sprintf("%d files %.2f MB\n", <-fileNumCh, float64(<-sizeTotalCh)/1e6) // 1024*1024 return result } // 遍历并统计某个特定目录的信息 // 核心实现函数,完成递归,统计等 func walkDir(dir string, filesizeCh chan<- int64, wg *sync.WaitGroup) { // 一:wg计数器减少 defer wg.Done() // 二:读取dir下的全部文件信息,并遍历 for _, fileinfo := range fileInfos(dir) { // 三:根据dir下的文件信息 if fileinfo.IsDir() { // 1. 如果目录,递归获取信息 // 子目录地址 subDir := filepath.Join(dir, fileinfo.Name()) // 递归调用,也是并发的,也需要wg统计 wg.Add(1) go walkDir(subDir, filesizeCh, wg) } else { // 2. 如果不是,就是文件,统计文件大小,放入channel filesizeCh <- fileinfo.Size() //Byte } } } // 获取某个目录下文件信息列表 func fileInfos(dir string) []fs.FileInfo { // 一,读取目录的全部文件 entries, err := os.ReadDir(dir) if err != nil { log.Println("WalkDir error:", err) return []fs.FileInfo{} // nil } // 二,获取文件的文件信息 // DirEntry to FileInfo infos := make([]fs.FileInfo, 0, len(entries)) for _, entry := range entries { // 如果获取文件信息无错误,存储到infos中。 if info, err := entry.Info(); err == nil { infos = append(infos, info) } } // 三,返回 return infos }