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 并发编程的主要内容,如果未能解决你的问题,请参考以下文章

Java 并发编程实战-创建和执行任务的最佳实践

Java 并发编程实战-创建和执行任务的最佳实践

并发编程模式

并发编程的几种形式

专家坐堂四种并发编程模型简介

专家坐堂四种并发编程模型简介