简单程序死锁,无法查明原因

Deadlock on simple program, can't identify the reason

我在以下代码中遇到死锁错误,但我无法确定原因。 该代码的目标是递归地获取给定文件夹中所有文件的 sha256 哈希值。 (最终目的是基于hash去除重复文件,作为学习项目)

为此,它生成一个 goroutine,该 goroutine 为每个文件生成另一个 goroutine 以获取哈希。哈希存储在通道中,并在代码末尾以简单循环读取。

根据打印件,检查所有文件,wg.Add 和完成似乎在正确的位置。

代码:

package main

import (
    "crypto/sha256"
    "fmt"
    "io"
    "io/fs"
    "log"
    "os"
    "path/filepath"
    "sync"
    "time"
)

func get_fsha256sum(filepath string) string {
    f, err := os.Open(filepath)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    return get_sha256sum(f)
}

func get_sha256sum(r io.Reader) string {
    h := sha256.New()
    if _, err := io.Copy(h, r); err != nil {
        log.Fatal(err)
    }
    return fmt.Sprintf("%x", h.Sum(nil))
}

func main() {
    start := time.Now()
    ch := make(chan string, 10000)
    var wg sync.WaitGroup

    var get_fsha256sum_wrap = func(filepath string) {
        start := time.Now()
        ch <- get_fsha256sum(filepath)
        fmt.Printf("%f-%s\n", filepath, time.Since(start))
        wg.Done()
    }

    var walk_func = func(filepath string, info fs.DirEntry, err error) error {
        wg.Add(1)
        if err != nil {
            return err
        }
        if info.IsDir() {
            return nil
        }

        go get_fsha256sum_wrap(filepath)
        return nil

    }

    var over_wrap = func(root string) {
        filepath.WalkDir(root, walk_func)
        wg.Wait()
        close(ch)
    }
    go over_wrap("/run/media/user/Red/pictures_master/2021/01")
    for v := range ch {
        fmt.Println(v)
    }
    fmt.Printf("%s", time.Since(start))
}

错误信息:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /home/user/go_phockup/main.go:66 +0x273

goroutine 18 [semacquire]:
sync.runtime_Semacquire(0xc0000b8018)
    /usr/local/go/src/runtime/sema.go:56 +0x45
sync.(*WaitGroup).Wait(0xc0000b8010)
    /usr/local/go/src/sync/waitgroup.go:130 +0x65
main.main.func3(0x4e3d3f, 0x2b)
    /home/go_phockup/main.go:62 +0x5e
created by main.main
    /home/user/go_phockup/main.go:65 +0x1bd
exit status 2

知道哪里出了问题吗?

您的等待组正在等待的 goroutine 数量超过了您创建的数量。 walk_func 添加到 wg,但可能 return 没有创建新的 goroutine。替换为:

var walk_func = func(filepath string, info fs.DirEntry, err error) error{
        if err != nil {
            return err
        }
        if info.IsDir() {
            return nil
        }
        
        wg.Add(1)
        go get_fsha256sum_wrap(filepath)
        return nil

        }