进入所有goroutine处于死锁状态

时间:2019-08-07 23:44:31

标签: go

似乎无法弄清楚为什么我收到错误消息:致命错误:所有goroutine都在睡眠中-死锁!

我怀疑我的区块中正在发生竞争状况,该竞争状况仅在关闭通道后才执行。

我认为添加同步WaitGroup会有所帮助,但这只是给了我这个僵局。我所看到的与我在网上看到的样本很接近,因此我不确定这里出了什么问题。

func S3UploadFolder(instance *confighelper.Instance, sess *session.Session, 
    srcFolder string, bucketName string) (err error) {

    log.Println("S3UploadFolder", srcFolder, bucketName)

    wg := &sync.WaitGroup{}

    // find files recursively
    walker := make(fileWalk)
    wg.Add(1)

    go func() {

        // Gather the files to upload by walking the path recursively
        if err := filepath.Walk(srcFolder, walker.Walk); err != nil {
            log.Fatalln("Walk failed:", err)
        }
        wg.Done()
        close(walker)

    }()
    wg.Wait()

    for path := range walker {
    // THE GO routine above needs to have finished by the time this for loop 
       // ranges over the channel
         fmt.Println(path)

 }



return
}


type fileWalk chan string

func (f fileWalk) Walk(path string, info os.FileInfo, err error) error {
    if err != nil {
        return err
    }
    if !info.IsDir() {
        f <- path
    }
    return nil
}

2 个答案:

答案 0 :(得分:3)

walker通道未缓冲。直到发送者和接收者准备就绪,才能在无缓冲通道上进行通信。

死锁是这样的:主goroutine通过调用wg.Done()等待Walker goroutine完成。步行者goroutine等待主goroutine在频道上接收。

通过删除与等待组有关的所有代码来修复程序。不需要等待组。直到行人goroutine关闭通道后,主goroutine中的通道范围才会完成。步行者goroutine直到步行完成才关闭通道。不需要其他协调。

您还可以通过删除goroutine和通道来修复代码:

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

    // Insert body of for path := range walker here ... 
    fmt.Println(path)

    return nil
})
if err != nil {
    log.Fatal(err)
}

另一种选择是创建一个缓冲通道,其容量大于将要遍历的文件数量,但这需要事先知道文件数量,并且与在片中收集文件名相比没有任何好处。

答案 1 :(得分:1)

按照所写(和所示)的操作,在运行wg.Wait()循环之前,您绝对不能 调用for path := range walker。循环终止时,您可以(但不需要)调用wg.Wait()。您根本不需要wg变量。

您的评论说:

// THE GO routine above needs to have finished by the time this for loop 
// ranges over the channel

但是for循环中没有什么需要完成函数,并且有一些东西(这里的总体策略)要求goroutine not 不为因为for循环仅在发送者(goroutine)关闭通道时结束,所以{{1}}循环将被终止。

(有关goroutine为什么在发送中被阻塞的原因,请参见Cerise Limón's answer