golang中sync.WorkGroup的问题

hhxx
  • 569

本人go的小白一个,在gopl中看到的一段代码,有些地方心里有不是特别明白,贴出请教一下大家

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() {
        wg.Wait()
        close(sizes)
    }()


    var total int64
    for size := range sizes {
        total += size
    }
    return total
}

thumbnail.ImageFile的具体内容不用关心,其原型如下

ImageFile(f string) (n string, e error)

closer那个goroutine,如果写在mainroutine中是会出错的,执行后报的是死锁的错。自己有点想法但是不太肯定,先说下自己的想法:

因为如果不在另一个goroutine中执行的话执行到wg.Wait()时就一直阻塞了main,因为没有接收channel的代码会执行(被Wait阻塞,后面的接收循环无法执行),所以除了第一个完成的goroutine所有的workerroutine都会被阻塞,造成死锁。
书中后面又说如果放在循环后面那么就不可达到,个人觉得是因为没有close,接收的循环就不会终止,只是会最后不断的接收到空值。
本人对并发不太懂,是做前端的,想自己学习一点后端,所以没有接触过正经的并发编程,请大家给我讲一讲吧!谢谢大家


回复
阅读 3.7k
3 个回答

我说说我的理解。。
主方法入参为channel,然后使用了sync.waitgroup来确保关闭sizes的队列关闭时从filenames过来的数据全部处理成功
filenames啥时候关闭?要是不关闭连第一个for都过不了就堵塞了吧。
处理完一条数据后wg.add和wg.done运行数量都为1,这时wg.wait就会过掉走close(sizes)这个时候在处理第二条数据,sizes <- info.Size()就会有问题吧,因为sizes已经关闭了。
假如说filenames写入N条数据后关闭。
第一个for接收到filenames的第一个消息后运行结束,在第二条数据还没来之前,wg.add和wg.done运行数量都为1,这时wg.wait就会过掉走close(sizes)这个时候在处理第二条数据,sizes <- info.Size()就会有问题吧。
假如只写入一条数据关闭

go func() {
        wg.Wait()
        close(sizes)
    }()

  var total int64
        for size := range sizes {
            total += size
        }
        return total
    

这两段代码的执行先后顺序是不一定的,能确定的是sizes <- info.Size()比defer wg.Done()早,
之后的是并发的话,所以这两段代码先后顺序就不是固定的了。
有可能会先执行了close(sizes)再运行for,这样子也会报错吧

luwei
  • 2
新手上路,请多包涵

sizes无缓存写和读都会阻塞,woker写sizes阻塞
wg.wait也会阻塞

TodLiuMeng
  • 1
新手上路,请多包涵

代码解析:
(1) filenames发送channel,只能进行读操作,不能用close关闭写权限
(2) 这里sizes为无缓存双向channel,所以只有调用close,sizes的range循环才能结束掉
(3) 所以wg.wait如果放在main goroutine range size之前 则由于sizes读取堵塞,wg.done()不执行,则wg.wait会一直堵塞下去
(4) 所以wg.wait如果放在main goroutine range size之后 则没有调用close sizes,则sizes一直range循环不停止

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