去所有的goroutines都睡着了僵局



>似乎无法弄清楚为什么我会收到错误消息:致命错误:所有goroutines都睡着了 - 死锁!

我怀疑我的块下面发生了竞争条件,该条件只能在通道关闭后执行。

我以为添加一个同步 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
}

walker通道未缓冲。 在发送方和接收方准备就绪之前,未缓冲通道上的通信不会继续。

僵局是这样的:主 goroutine 通过调用 wg 等待步行者 goroutine 完成。完成((。步行者 goroutine 在主 goroutine 上等待在通道上接收。

通过删除与等待组相关的所有代码来修复程序。不需要等待组。在主 goroutine 中的通道上的范围在通道被步行者 goroutine 关闭之前不会完成。步行者 goroutine 在步行完成之前不会关闭通道。不需要其他协调。

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

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)
}

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

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

您的评论说:

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

但是for循环中没有任何内容需要完成函数,并且有一些东西(这里的总体策略(要求在发送中阻塞 goroutine,因为for循环只有在发送方(GoRoutine(关闭通道时才会完成。

(参见 Cerise Limón 的答案,了解为什么 goroutine 在发送中被阻塞。

最新更新