Go语言关于 goroutines 泄漏的一个问题

问题描述

在阅读《go语言圣经》这本数中,对 “并发的循环” 这个章节中,使用 WaitGoup 处理 goroutines 泄漏的描述未看懂,请各位指教。

书中描述说:

如果等待操作被放在了main goroutine中,在循环之前,这样的话就永远都不会结束了

我不明白这里为什么一定要把 wg.Wait() 放到一个goroutine中去执行呢?为什么放到 main goroutine 中,就永远不会结束了?

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

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
            sizes <- info.Size()
        }(f)
    }

    // closer
    go func() { // 为什么一定要在goroutine中进行执行?
        wg.Wait()
        close(sizes)
    }()

    var total int64
    for size := range sizes {
        total += size
    }
    return total
}
阅读 731
评论
    2 个回答
    • 1.5k

    放在main goroutine中会死锁,无论是在chan读取之前还是之后

    本质上是因为不知道会有多少个worker goroutine..(在这个例子中就是文件的数目)


    放在channel读取之前, worker的写channel会阻塞

    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) 
                sizes <- info.Size()  // 阻塞在这里,因为没有办法读取
            }(f)
        }
    
        // closer
        wg.Wait()      // 阻塞在这里
        close(sizes)
    
        var total int64
        for size := range sizes {
            total += size
        }
        return total
    }

    放在读取channel之后,会一直阻塞在读取channel的地方

    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) 
                sizes <- info.Size()  
            }(f)
        }
    
    
    
        var total int64
        for size := range sizes {   // 阻塞在这里
            total += size
        }
        
        // closer
        wg.Wait()     
        close(sizes)
        
        return total
    }
      • 211

      备忘
      放在之前:
      worker 中的 sizes <- info.Size() 被阻塞 ,无法走到defer wg.Done()wg.Wait() 也被阻塞,那么无法走到

      for size := range sizes {  
            total += size      
      }

      sizes没法被读取,导致死锁

      放在之后:
      因为没有close(sizes)

      for size := range sizes {  
            total += size      
      }

      一直在阻塞,导致死锁

      https://stackoverflow.com/que...

        撰写回答

        登录后参与交流、获取后续更新提醒

        相似问题
        推荐文章