dsen53898 2018-10-03 16:58
浏览 22
已采纳

为什么会出现致命错误:所有goroutine都在睡眠中-死锁! 在这段代码中?

This is in reference to following code in The Go Programming Language - Chapter 8 p.238 copied below from this link

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

Why do we need to put the closer in a goroutine? Why can't below work?

// closer
        // go func() {
        fmt.Println("waiting for reset")
                wg.Wait()
        fmt.Println("closing sizes")
                close(sizes)
        // }()

If I try running above code it gives:

waiting for reset
3547
2793
fatal error: all goroutines are asleep - deadlock!

Why is there a deadlock in above? fyi, In the method that calls makeThumbnail6 I do close the filenames channel

  • 写回答

1条回答 默认 最新

  • dongtuo1482 2018-10-03 17:25
    关注

    Your channel is unbuffered (you didn't specify any buffer size when make()ing the channel). This means that a write to the channel blocks until the value written is read. And you read from the channel after your call to wg.Wait(), so nothing ever gets read and all your goroutines get stuck on the blocking write.

    That said, you do not need WaitGroup here. WaitGroups are good when you don't know when your goroutine is done, but you are sending results back, so you know. Here is a sample code that does a similar thing to what you are trying to do (with fake worker payload).

    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
    ", 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
    ", msg)
            }
        }
    }
    

    Test it in the playground here https://play.golang.org/p/RtMkYbAqtGO

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 【急】在线问答CNC雕刻机的电子电路与编程
  • ¥60 在mc68335芯片上移植ucos ii 的成功工程文件
  • ¥15 笔记本外接显示器正常,但是笔记本屏幕黑屏
  • ¥15 Python pandas
  • ¥15 蓝牙硬件,可以用哪几种方法控制手机点击和滑动
  • ¥15 生物医学数据分析。基础课程就v经常唱课程舅成牛逼
  • ¥15 云环境云开发云函数对接微信商户中的分账功能
  • ¥15 空间转录组CRAD遇到问题
  • ¥20 materialstudio计算氢键脚本问题
  • ¥15 有没有代做有偿主要做数据可视化部分即可(2023全国高考更省一本线理科类)