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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}