这是参考《 Go编程语言-第8章,第238页,从下面的this链接复制过来的以下代码》
key
为什么我们需要将闭门器放在goroutine中?为什么下面不能工作?
// makeThumbnails6 makes thumbnails for each file received from the channel.
// It returns the number of bytes occupied by the files it creates.
func makeThumbnails6(filenames <-chan string) int64 {
sizes := make(chan int64)
var wg sync.WaitGroup // number of working goroutines
for f := range filenames {
wg.Add(1)
// worker
go func(f string) {
defer wg.Done()
thumb, err := thumbnail.ImageFile(f)
if err != nil {
log.Println(err)
return
}
info, _ := os.Stat(thumb) // OK to ignore error
fmt.Println(info.Size())
sizes <- info.Size()
}(f)
}
// closer
go func() {
wg.Wait()
close(sizes)
}()
var total int64
for size := range sizes {
total += size
}
return total
}
如果我尝试运行上面的代码,它将给出:
等待重置
3547
2793
致命错误:所有goroutine都在睡眠中-死锁!
为什么上面有一个死锁? fyi,在调用// closer
// go func() {
fmt.Println("waiting for reset")
wg.Wait()
fmt.Println("closing sizes")
close(sizes)
// }()
的方法中,我确实关闭了makeThumbnail6
频道
答案 0 :(得分:1)
您的通道未缓冲(在对通道进行make()处理时未指定任何缓冲区大小)。这意味着对通道的写入将一直阻塞,直到读取了写入的值。而且,在调用wg.Wait()之后,您从通道中进行读取,因此不会读取任何内容,并且所有goroutine都会卡在阻塞写入中。
也就是说,您在这里不需要WaitGroup。当您不知道goroutine何时完成时,WaitGroups很好,但是您将结果发送回去,所以您知道。这是一个示例代码,与您尝试执行的操作(具有伪造的工作负载)具有相似的作用。
package main
import (
"fmt"
"time"
)
func main() {
var procs int = 0
filenames := []string{"file1", "file2", "file3", "file4"}
mychan := make(chan string)
for _, f := range filenames {
procs += 1
// worker
go func(f string) {
fmt.Printf("Worker processing %v\n", f)
time.Sleep(time.Second)
mychan <- f
}(f)
}
for i := 0; i < procs; i++ {
select {
case msg := <-mychan:
fmt.Printf("got %v from worker channel\n", msg)
}
}
}
在https://play.golang.org/p/RtMkYbAqtGO这里的操场上进行测试
答案 1 :(得分:0)
尽管提出问题已经有一段时间了,但我遇到了同样的问题。最初,我的主要内容如下:
func main() {
filenames := make(chan string, len(os.Args))
for _, f := range os.Args[1:] {
filenames <- f
}
sizes := makeThumbnails6(filenames)
close(filenames)
log.Println("Total size: ", sizes)}
由于range filenames
中的调用makeThumbnails6
是同步的,因此该版本陷入僵局,因此从不调用main中的close(filenames)
。 makeThumbnails6
中的频道未缓冲,因此goroutines在尝试发送回大小时会阻塞。
解决方案是在对main进行函数调用之前移动close(filenames)
。
答案 2 :(得分:-1)
代码错误。简而言之,通道sizes
是无缓冲的。要解决此问题,我们需要在创建sizes
时使用具有足够容量的缓冲通道。如图所示,单线修复就足够了。在这里,我只是简单地假设1024足够大。
func makeThumbnails6(filenames chan string) int64 {
sizes := make(chan int64, 1024) // CHANGE
var wg sync.WaitGroup // number of working goroutines
for f := range filenames {
wg.Add(1)
// worker
go func(f string) {
defer wg.Done()
thumb, err := thumbnail.ImageFile(f)
if err != nil {
log.Println(err)
return
}
info, _ := os.Stat(thumb) // OK to ignore error
fmt.Println(info.Size())
sizes <- info.Size()
}(f)
}
// closer
go func() {
wg.Wait()
close(sizes)
}()
var total int64
for size := range sizes {
total += size
}
return total
}