在 GOLANG 中尽可能快地递归遍历所有文件夹中的所有文件

Loop through all files in all folders recursively as fast as possible in GOLANG

我遇到了一个问题,即使在论坛上花了一天时间,我仍然无法完全理解和解决。

所以在这里,我创建了一个循环遍历所有文件夹及其子文件夹的函数,它有 2 个子函数: - 对于找到的每个文件,列出文件的名称。 - 对于找到的每个文件夹,重新启动相同的父函数以再次查找子文件和文件夹。

为了简单起见,宏以递归方式列出了树中的所有文件。但我的目标是尽可能快地完成,所以我每次遇到新文件夹时 运行 一个新的 goroutine。

问题:
我的问题是,当树结构太大(文件夹和子文件夹中的文件夹太多......)时,脚本会生成太多线程,因此会给我一个错误。所以我增加了这个限制,但突然间它不再需要 :/

所以我的问题是,如何制作适合我的代码的辅助系统(带池大小)? 怎么看都看不出来,比如生成新的goroutines到一定的限度,清空缓冲区的时间。


源代码:
https://github.com/LaM0uette/FilesDIR/tree/V0.5

主要内容:

package main

import (
    "FilesDIR/globals"
    "FilesDIR/task"
    "fmt"
    "log"
    "runtime/debug"
    "sync"
    "time"
)

func main() {
    timeStart := time.Now()
    debug.SetMaxThreads(5 * 1000)

    var wg sync.WaitGroup

    // task.DrawStart()

    /*
        err := task.LoopDir(globals.SrcPath)
        if err != nil {
            log.Print(err.Error())
        }
    */

    err := task.LoopDirsFiles(globals.SrcPath, &wg) // globals.SrcPath = My path with ~2000000 files ( this is a serveur of my entreprise)
    if err != nil {
        log.Print(err.Error())
    }

    wg.Wait()

    fmt.Println("FINI: Nb Fichiers: ", task.Id)

    timeEnd := time.Since(timeStart)
    fmt.Println(timeEnd)
}

任务:

package task

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "strings"
    "sync"
    "time"
)

var Id = 0

// LoopDir TODO: Code à supprimer / Code to delete
func LoopDir(path string) error {
    var wg sync.WaitGroup

    countDir := 0

    err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        if info.IsDir() {
            wg.Add(1)
            countDir++

            go func() {
                err := loopFiles(path, &wg)
                if err != nil {
                    log.Println(err.Error())
                }
            }()
        }

        return nil
    })
    if err != nil {
        return err
    }

    wg.Wait()
    fmt.Println("Finished", countDir, Id)
    return nil
}

// loopFiles TODO: Code à supprimer / Code to delete
func loopFiles(path string, wg *sync.WaitGroup) error {

    files, err := ioutil.ReadDir(path)
    if err != nil {
        wg.Done()
        return err
    }

    for _, file := range files {
        if !file.IsDir() {
            go fmt.Println(file.Name())
            Id++
        }
    }

    wg.Done()
    return nil
}

func LoopDirsFiles(path string, wg *sync.WaitGroup) error {
    wg.Add(1)
    defer wg.Done()

    files, err := ioutil.ReadDir(path)
    if err != nil {
        return err
    }

    for _, file := range files {
        if !file.IsDir() && !strings.Contains(file.Name(), "~") {
            fmt.Println(file.Name(), Id)
            Id++
        } else if file.IsDir() {
            go func() {
                err = LoopDirsFiles(filepath.Join(path, file.Name()), wg)
                if err != nil {
                    log.Print(err)
                }
            }()
            time.Sleep(20 * time.Millisecond)
        }
    }
    return nil
}

如果您不想使用任何外部包,您可以创建一个单独的工作程序来处理文件,然后启动任意数量的工作程序。之后,在你的主线程中递归地进入树,并将工作发送给工人。如果任何工人“有时间”,它会从工作通道中挑选以下工作并处理它。

var (
    wg   *sync.WaitGroup
    jobs chan string = make(chan string)
)

func loopFilesWorker() error {
    for path := range jobs {
        files, err := ioutil.ReadDir(path)
        if err != nil {
            wg.Done()
            return err
        }

        for _, file := range files {
            if !file.IsDir() {
                fmt.Println(file.Name())
            }
        }
        wg.Done()
    }
    return nil
}

func LoopDirsFiles(path string) error {
    files, err := ioutil.ReadDir(path)
    if err != nil {
        return err
    }
    //Add this path as a job to the workers
    //You must call it in a go routine, since if every worker is busy, then you have to wait for the channel to be free.
    go func() {
        wg.Add(1)
        jobs <- path
    }()
    for _, file := range files {
        if file.IsDir() {
            //Recursively go further in the tree
            LoopDirsFiles(filepath.Join(path, file.Name()))
        }
    }
    return nil
}

func main() {
    //Start as many workers you want, now 10 workers
    for w := 1; w <= 10; w++ {
        go loopFilesWorker()
    }
    //Start the recursion
    LoopDirsFiles(globals.SrcPath)
    wg.Wait()
}