https://golang.org/pkg/sync/#example_WaitGroup
Хотя с каналами и быстрее, но я предпочитаю контроль через wg.
upd.
Контролируйте количество рутин. Раздробите приложение на маленькие функции.
в главном сразу запускается 10 рутин на выполнение запросов, 10 рутин на анализ ответа от сторонего сервиса, 10 рутин ещё чего-либо.
Рутины для выполнения запросов принимают, например, из единого канала урл, на который надо сделать запрос. Отправляете урл в канал, свободная рутина хватает и выполняет запрос, получет ответ, передает в другой канал, из которого уже хватает свободная рутина для анализа ответа. Ну и т.д.
Вот топорный пример, как делаю я
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
urls := []string{"http://google.ru", "http://vk.com", "http://ya.ru"}
chanUrls := make(chan string, 100)
chanRes := make(chan http.Response, 100)
chanPrint := make(chan string, 100)
for _, url := range urls {
chanUrls <- url
}
close(chanUrls)
go httpGet(chanUrls, chanRes)
go analys(chanRes, chanPrint)
printRes(chanPrint)
}
func httpGet(chanUrls <-chan string, chanRes chan<- http.Response) {
defer close(chanRes)
for url := range chanUrls {
res, err := http.DefaultClient.Get(url)
if err == nil {
chanRes <- *res
}
}
}
func analys(chanRes <-chan http.Response, chanPrint chan<- string) {
defer close(chanPrint)
for res := range chanRes {
body, err := ioutil.ReadAll(res.Body)
if err == nil {
chanPrint <- string(body)
}
}
}
func printRes(chanPrint <-chan string) {
for pr := range chanPrint {
fmt.Println(pr)
}
}