068-Go 并发编程
Posted --Allen--
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了068-Go 并发编程相关的知识,希望对你有一定的参考价值。
并发编程的难点在于异常处理。今天我们继续研究缩略图的并发编程,还记得之前留下的问题吗?我们的程序没有对程序返回的错误做特殊照顾。在服务器开发领域,这样的程序的显然不够健壮。
1. 让程序能够处理错误
之前我们的 makeThumbnails 函数没有返回值,现在我们给它添加一个 error 类型的返回值。下面这个程序在 demo04.go 中。
func makeThumbnails(filenames []string) error
// errors 是一个无缓冲的 channel
errors := make(chan error)
for _, f := range filenames
go func(f string)
_, err := thumbnail.ImageFile(f)
errors <- err
(f)
for range filenames
if err := <-errors; err != nil
return err
return nil
func main()
now := time.Now()
err := makeThumbnails(os.Args[1:])
if err != nil
fmt.Println(err)
fmt.Printf("elapse:%.3f ms\\n", 1000*time.Since(now).Seconds())
运行一下:
图1 运行结果
这次我换成了 4 核心 3.1GHz 的 cpu 主机,所以速度看起来比上一次实验要快很多(上次是 2 核心 2.3 GHz)。
这一次,看起来我们的程序已经支持了错误处理,but … 这个程序还是有 bug.
2. goroutine leak
简单分析一下:
假设 makeThumbnails 函数在处理第一幅图像时出错了,于是在执行
for range filenames
if err := <-errors; err != nil
return err
的时候,makeThumbnails 会直接返回错误。看起来似乎没什么问题?是的,程序出错了,就应该返回错误。不过我们要讨论的问题不是这里,而是 makeThumbnails 创建的那几个 goroutines:
for _, f := range filenames
go func(f string)
_, err := thumbnail.ImageFile(f)
errors <- err
(f)
- 假设某个协程在执行 thumbnail.ImageFile 时返回错误,将 err 推入 erros channel,然后该协程结束。
- for range 部分从 errors 中读取到 err 错误,直接返回。
- makeThumbnails 已经因为错误返回,其它所有创建的协程将阻塞在
errors <- err
上无法返回,因为 errors 没有缓冲,而且也没有任何协程读取
这有什么影响呢?在 Golang 里,这种情况叫 groutine 泄露(goroutine leak)。在服务器开发领域,这是一件非常重要而且严肃的事情,尽管初期它对你的程序功能没什么影响,但是随着运行时间越来越长,最终你的服务就会因为资源耗尽而 crash.
在我们这个例子中,解决这个问题的办法非常简单,我们使用一个带缓冲的 errors 就行了,你可以修改成下面这样:
func makeThumbnails(filenames []string) error
// errors 是一个带缓冲的 channel,大小和要处理的文件个数一致
errors := make(chan error, len(filenames))
for _, f := range filenames
go func(f string)
_, err := thumbnail.ImageFile(f)
errors <- err
(f)
for range filenames
if err := <-errors; err != nil
return err
return nil
这样即便 makeThumbnails 因为错误提前返回,其它协程也能正常结束。
3. 总结
- 知道什么是 goroutine 泄露
- 知道如何解决 goroutine 泄露
以上是关于068-Go 并发编程的主要内容,如果未能解决你的问题,请参考以下文章