Golang Goroutine 错误“所有 goroutines 都在休眠 - 死锁!”

Posted

技术标签:

【中文标题】Golang Goroutine 错误“所有 goroutines 都在休眠 - 死锁!”【英文标题】:Golang Goroutine Error "all goroutines are asleep - deadlock!" 【发布时间】:2018-05-28 19:59:45 【问题描述】:

我正在尝试制作一个代码,以从文件夹链接我的所有文件进行扫描,并根据他的内容和他的姓名使用正则表达式按他的大小制作“前 10 名”。文件。通过它的内容,我用 goroutines 制作频道,但我不明白为什么每次我的 goroutines 都被锁定。这是我的代码:

package main

import (
    "flag"
    "fmt"
    "io/ioutil"
    "regexp"
    "runtime"
    "sort"
    "sync"
    "time"
)

var rName = ".php"
var rContent = "php"
var maxSize, minSize int64
var files_ten []File

func main() 
    start := time.Now()

    channelOne := make(chan File)
    channelTwo := make(chan File)

    var wg sync.WaitGroup
    var path string
    flag.StringVar(&path, "path", "", "Path to folder")
    flag.Parse()
    fmt.Println("Path=", path)

    for i := 0; i < runtime.NumCPU(); i++ 
        go check(channelOne, channelTwo, &wg)
    

    go top10(channelTwo, &wg)

    wg.Wait()

    getFolder(path, channelOne, &wg)

    fmt.Println("top 10", files_ten)
    t := time.Now()
    current := t.Sub(start)
    fmt.Println(current)



type File struct 
    Size int64
    Name string
    Path string


func (this File) GetSize() int64 
    return this.Size


func getFolder(path string, channelOne chan File, wg *sync.WaitGroup) 
    folder, err := ioutil.ReadDir(path)

    if err != nil 
        fmt.Println("Error:", err)
        return
    

    for _, data := range folder 
        if data.IsDir() 
            var newFolder string = path + data.Name() + "/"
            getFolder(newFolder, channelOne, wg)
         else 
            wg.Add(1)
            channelOne <- FileSize: data.Size(), Name: data.Name(), Path: path
        
    


func check(channelOne chan File, channelTwo chan File, wg *sync.WaitGroup) 
    for 
        file := <-channelOne
        rName := regexp.MustCompile(rName)

        maxSize = 10000
        minSize = 0

        if rName.MatchString(file.Name) 
            if file.Size <= maxSize && file.Size >= minSize 
                f, err := ioutil.ReadFile(file.Path + "/" + file.Name)

                if err != nil 
                    fmt.Println("Error:", err)
                    return
                
                rContent := regexp.MustCompile(rContent)
                if rContent.MatchString(string(f)) 
                    channelTwo <- file
                 else 
                    wg.Done()
                
             else 
                wg.Done()
            
         else 
            wg.Done()
        
    


func sortFilesFromBiggestToLowerSize(arrayFile []File) []File 
    sort.Slice(arrayFile, func(i, j int) bool 
        return arrayFile[i].Size > arrayFile[j].Size
    )
    return arrayFile


func top10(channelTwo chan File, wg *sync.WaitGroup) []File 
    for 
        f := <-channelTwo

        if len(files_ten) == 10 
            if f.Size > files_ten[0].Size || f.Size >
                files_ten[len(files_ten)-1].Size 
                files_ten = files_ten[:len(files_ten)-1]
                files_ten = append(files_ten, f)
                return sortFilesFromBiggestToLowerSize(files_ten)
            
         else 
            sortFilesFromBiggestToLowerSize(files_ten)
            return append(files_ten, f)
        
        wg.Done()
        return files_ten
    

这是每次编译时的错误:

go run filebysize.go --path=C:/wamp64/www/symfony/init/cours1/
Path= C:/wamp64/www/symfony/init/cours1/
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.getFolder(0xc04210a3c0, 0x3d, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:72 +0x28a
main.getFolder(0xc04210a200, 0x32, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:69 +0x151
main.getFolder(0xc04200e6c0, 0x26, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:69 +0x151
main.getFolder(0xc042051f57, 0x22, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:69 +0x151
main.main()
    C:/Users/Sahra/Documents/go/display/filebysize.go:37 +0x2e0

goroutine 19 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 20 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 21 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 22 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 23 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 24 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 25 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 26 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d
exit status 2

【问题讨论】:

这是很多代码要理解,但有些东西看起来不太对劲:top10 的返回值被忽略了,看起来 waitgroup 用于计算除执行 goroutines 之外的其他内容。 你是对的,问题是在函数top 10中,我不需要返回任何东西,包括在if else语句中,非常感谢!!!!!!!!!!!!!!! !!!!!!!!! 【参考方案1】:

您正尝试在channelOne 上发送,但直到 wg.Done 之后才从其中读取任何内容,因此出现了死锁:尝试发送给它的例程必须等到可以接收某些东西,而这永远不会发生。

另外,您的 WaitGroup 使用已关闭;您应该在启动每个要等待的 goroutine 之前调用 Add,然后在 goroutine 结束时调用 Done。单个goroutine不应在循环中调用AddDone,如果没有关联的Add调用,则goroutine不应调用Done

看起来您有多个永远不会退出的for 循环;他们没有条件也没有breaks。

您还可以更简单地循环频道。您可以替换如下结构:

for 
    file := <-channelOne

用更简单的:

for file := range channelOne 

这还有一个额外的好处,即当您覆盖的通道关闭时,循环将退出,允许您使用关闭通道作为消费者可以停止的信号。

【讨论】:

以上是关于Golang Goroutine 错误“所有 goroutines 都在休眠 - 死锁!”的主要内容,如果未能解决你的问题,请参考以下文章

Golang的sync.WaitGroup 实现逻辑和源码解析

goroutine的使用与常见错误

Golang之goroutine

golang goroutine例子[golang并发代码片段]

golang Golangでgoroutine

golang的panic与recover