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 交替优化波束形成和ris反射角使保密速率最大化
  • ¥15 树莓派与pix飞控通信
  • ¥15 自动转发微信群信息到另外一个微信群
  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程