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

大愚Talk
  • 3.1k

问题描述

在阅读《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
}
回复
阅读 972
2 个回答
✓ 已被采纳

放在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
}

备忘
放在之前:
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...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏