似乎无法弄清楚为什么我收到错误消息:致命错误:所有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
}
答案 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。