GO并发统计文件大小

复制代码
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)
      // 并发的编历统计每个目录的信息
      go walkDir(dir,fileSizeCh,wg)
   }

   // 启动累计运算的Goroutine
   // 1.用户关闭fileSizeCh
   go func(wg *sync.WaitGroup) {
      // 等待统计工作完成
      wg.Wait()
      // 关闭fileSizeCh
      close(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)
   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()
      }
   }
}

// 获取某个目录下的文件信息列表
func fileInfos(dir string)[]fs.FileInfo  {
   // 1.读取目录的全部文件
   entries,err:=os.ReadDir(dir)
   if err!=nil{
      log.Println("WalkDir error:",err)
      return []fs.FileInfo{}
   }
   // 2.如获取文件的文件信息
   // 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)
      }
   }
   // 3.返回
   return infos
}
相关推荐
颜进强2 分钟前
04 - 一张图看懂 OpenSpec 变更的一生:从创建到归档
前端·后端·ai编程
Cobyte3 分钟前
Claude Code 的 Task System 的实现原理
后端·aigc·ai编程
小强19883 分钟前
useEffect 完整使用指南:依赖数组、闭包陷阱、清理函数实战
后端
智驭未来掌门人3 分钟前
在windows下快速搭建go2rtc流媒体平台
后端
大白8016 分钟前
为什么不要滥用 useMemo 和 useCallback?过度优化反而更卡
后端
萧瑟余晖37 分钟前
Java深入解析篇三十六之分布式系统详解
java·开发语言·分布式
神奇小汤圆1 小时前
Codex 子 Agent 配置指南:让 Sol 当军师,Luna 当搬砖工
后端
ClouGence1 小时前
2026 年 4 款数据库管理工具推荐:免费、开源、付费怎么选?
数据库·后端·开源
ZJU_统一阿萨姆1 小时前
【推理优化进阶】调度器的数学内核:排队论、SLO 与在线决策
开发语言·人工智能·语言模型·系统架构·vllm
大勇前进1 小时前
React 中 State 为什么不能直接修改?很多新手一直没懂
后端