本人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,接收的循环就不会终止,只是会最后不断的接收到空值。
本人对并发不太懂,是做前端的,想自己学习一点后端,所以没有接触过正经的并发编程,请大家给我讲一讲吧!谢谢大家
我说说我的理解。。
主方法入参为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()就会有问题吧。
假如只写入一条数据关闭
和
这两段代码的执行先后顺序是不一定的,能确定的是sizes <- info.Size()比defer wg.Done()早,
之后的是并发的话,所以这两段代码先后顺序就不是固定的了。
有可能会先执行了close(sizes)再运行for,这样子也会报错吧