drtldt55533 2014-12-20 10:15
浏览 32
已采纳

Golang:关闭频道

I've created a simple channel to make asynchronous HTTP requests based on the following example:

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

What would be the best pattern to close the channel, once all the requests have finished?

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)
        }
    }
}
  • 写回答

1条回答 默认 最新

  • doumi1884 2014-12-20 12:04
    关注

    The code, written like this, will produce a deadlock. But, the channel does not have necessarily to be closed. There are multiple ways to solve this issue.

    For instance, you could replace the for/select loop by:

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

    Here we assume that the potential timeouts are managed in each goroutine.

    Another solution, which really relies on closing the channel could be written as follows:

    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)
        }
    }
    

    Note that compared the initial code, the request variable is not accessed from the goroutine, but passed as a parameter. The output data structure posted via the channel is therefore consistent. This was an issue in the initial code. See more information about this specific topic at: https://github.com/golang/go/wiki/CommonMistakes

    Yet another solution would be to count the responses in the goroutines using an atomic counter, and explicitly close the channel when the counter reaches the limit. But dealing with sync/atomic is often error-prone, so it is probably not a good idea here.

    Finally, sometimes you need to get more control in order to properly manage timeouts, errors, etc ... The tomb package can help you to manage the lifecycle of the goroutines in a safe way.

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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮