You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
3.0 KiB

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
}