为什么会出现致命错误:所有的 goroutines 都睡着了——死锁!在这段代码中?

这是参考 The Go Programming Language - Chapter 8 p.238 中的以下代码,从该链接下面复制


// 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

}

为什么我们需要将 closer 放在 goroutine 中?为什么下面不能工作?


// closer

        // go func() {

        fmt.Println("waiting for reset")

                wg.Wait()

        fmt.Println("closing sizes")

                close(sizes)

        // }()

如果我尝试运行上面的代码,它会给出:


等待重置

3547

2793

致命错误:所有 goroutines 都睡着了 - 死锁!


为什么上面会出现死锁?fyi,在调用的方法中makeThumbnail6我确实关闭了filenames频道


红糖糍粑
浏览 131回答 3
3回答

手掌心

您的频道是无缓冲的(您在 make()ing 频道时没有指定任何缓冲区大小)。这意味着写入通道会阻塞,直到写入的值被读取。在调用 wg.Wait() 之后,您从通道读取数据,因此不会读取任何内容,并且您的所有 goroutine 都会卡在阻塞写入中。也就是说,您在这里不需要 WaitGroup。当您不知道 goroutine 何时完成时,WaitGroups 很有用,但您正在发回结果,所以您知道。这是一个示例代码,它执行与您尝试执行的操作类似的操作(使用伪造的工作负载)。package mainimport (    "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)        }    }}

aluckdog

虽然提出问题已经有一段时间了,但我遇到了同样的问题。最初我的主要内容如下所示:func main() {&nbsp; &nbsp;filenames := make(chan string, len(os.Args))&nbsp; &nbsp;for _, f := range os.Args[1:] {&nbsp; &nbsp; &nbsp; &nbsp;filenames <- f&nbsp; &nbsp;}&nbsp; &nbsp;sizes := makeThumbnails6(filenames)&nbsp; &nbsp;close(filenames)&nbsp; &nbsp;log.Println("Total size: ", sizes)}此版本死锁,因为调用range filenames是makeThumbnails6同步的,因此close(filenames)in main 从未被调用。输入的通道makeThumbnails6是无缓冲的,因此 goroutines 在尝试发回大小时会阻塞。close(filenames)解决方案是在 main 中进行函数调用之前移动。

哔哔one

代码错误。简而言之,通道sizes是无缓冲的。要修复它,我们需要在创建时使用具有足够容量的缓冲通道sizes。一行修复就足够了,如图所示。这里我只是做了一个简单的假设,1024足够大。func makeThumbnails6(filenames chan string) int64 {&nbsp; &nbsp; sizes := make(chan int64, 1024)&nbsp; &nbsp; // CHANGE&nbsp;&nbsp; &nbsp; var wg sync.WaitGroup // number of working goroutines&nbsp; &nbsp; for f := range filenames {&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; // worker&nbsp; &nbsp; &nbsp; &nbsp; go func(f string) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; thumb, err := thumbnail.ImageFile(f)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println(err)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; info, _ := os.Stat(thumb) // OK to ignore error&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(info.Size())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sizes <- info.Size()&nbsp; &nbsp; &nbsp; &nbsp; }(f)&nbsp; &nbsp; }&nbsp; &nbsp; // closer&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; &nbsp; &nbsp; close(sizes)&nbsp; &nbsp; }()&nbsp; &nbsp; var total int64&nbsp; &nbsp; for size := range sizes {&nbsp; &nbsp; &nbsp; &nbsp; total += size&nbsp; &nbsp; }&nbsp; &nbsp; return total}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go