2014-12-20 2 views
1

я создал простой канал, чтобы сделать Асинхронный HTTP запросов основано на следующем примере:GoLang: Закрытие канала

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

ответ

5

код, написанные, как это, произведет тупик. Но канал не обязательно должен быть закрыт. Существует несколько способов решения этой проблемы.

Например, вы могли бы заменить на/выберите цикл по:

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

Еще одно решение - подсчитать ответы в горутах с использованием атомного счетчика и явно закрыть канал, когда счетчик достигнет предела. Но работа с sync/atomic часто подвержена ошибкам, поэтому, вероятно, это не очень хорошая идея.

Наконец, иногда вам нужно получить больше контроля, чтобы правильно управлять тайм-аутами, ошибками и т. Д. Пакет гробницы может помочь вам безопасно управлять жизненным циклом goroutines.

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

+0

Awesome, спасибо за подробный ответ. – patrickandroid

Смежные вопросы