【问题标题】:Closing a channel关闭通道
【发布时间】:2015-02-19 03:57:57
【问题描述】:

我根据以下示例创建了一个简单的通道来发出异步 HTTP 请求:

http://matt.aimonetti.net/posts/2012/11/27/real-life-concurrency-in-go/

一旦所有请求都完成,关闭通道的最佳模式是什么?

type HttpRequest struct {
    url        string
}

type HttpResponse struct {
    request  HttpRequest
    response *http.Response
    err      error
}

func asyncHttpGets(requests []HttpRequest) {
    ch := make(chan *HttpResponse)
    for _, request := range requests {
        go func(url string) {
            resp, err := http.Get(url)
            ch <- &HttpResponse{request, resp, err}
        }(request.url)
    }

    for {
        select {
        case r := <-ch:
            processResponse(r)
        }
    }
}

【问题讨论】:

    标签: concurrency go channel


    【解决方案1】:

    这样写的代码会产生死锁。但是,通道不一定要关闭。有多种方法可以解决此问题。

    例如,您可以将 for/select 循环替换为:

    n := len(requests)
    for r := range ch {
        processResponse(r)
        n--
        if n == 0 {
            break
        }
    }
    

    这里我们假设潜在的超时在每个 goroutine 中进行管理。

    另一个真正依赖于关闭通道的解决方案可以写成如下:

    func asyncHttpGets(requests []HttpRequest) {
    
        ch := make(chan *HttpResponse)
        var wg sync.WaitGroup
        for _, request := range requests {
            wg.Add(1)
            go func(r HttpRequest) {
                defer wg.Done()
                resp, err := http.Get(r.url)
                ch <- &HttpResponse{r, resp, err}
            }(request)
        }
    
        go func() {
            wg.Wait()
            close(ch)
        }()
        for r := range ch {
            processResponse(r)
        }
    }
    

    请注意,对比初始代码,请求变量不是从 goroutine 访问的,而是作为参数传递的。因此,通过通道发布的输出数据结构是一致的。这是初始代码中的一个问题。在以下位置查看有关此特定主题的更多信息:https://github.com/golang/go/wiki/CommonMistakes

    另一种解决方案是使用原子计数器计算 goroutine 中的响应,并在计数器达到限制时显式关闭通道。但是处理 sync/atomic 往往容易出错,所以这里可能不是一个好主意。

    最后,有时您需要获得更多控制权才能正确管理超时、错误等...... tomb 包可以帮助您以安全的方式管理 goroutine 的生命周期。

    https://github.com/go-tomb/tomb/tree/v2

    【讨论】:

      猜你喜欢
      • 2018-05-09
      • 2014-07-14
      • 2021-05-17
      • 2014-01-07
      • 2016-08-09
      • 1970-01-01
      • 1970-01-01
      • 2022-11-17
      • 2019-03-15
      相关资源
      最近更新 更多