Go で errgroup を使って直列 vs 並列
直列 vs 並列シリーズ第 3 弾。 あんまり errgroup を使ったサンプルがなかったのでやってみた。
package main import ( "fmt" "io/ioutil" "net/http" "sync" "time" "golang.org/x/sync/errgroup" ) func createUrl(sec int) string { return fmt.Sprintf("http://httpbin.org/delay/%d", sec) } func requestBody(url string) (string, error) { response, err := http.Get(url) if (err != nil) { return "", err } defer response.Body.Close() bytes, err := ioutil.ReadAll(response.Body) if (err != nil) { return "", err } return string(bytes), nil } // errgroup を使って非同期に関数を実行し、結果を待つ func awaitAll(funcs []func() error) error { eg := errgroup.Group{} for _, fn := range funcs { eg.Go(fn) } if err := eg.Wait(); err != nil { return err } return nil } func serial(urls []string) ([]string, error) { bodies := make([]string, len(urls)) for _, url := range urls { body, err := requestBody(url); if (err != nil) { return nil, err } // fmt.Println(body) bodies = append(bodies, body) } return bodies, nil } func parallel(urls []string) ([]string, error) { mutex := new(sync.Mutex) bodies := make([]string, 0, len(urls)) funcs := make([]func() error, 0, len(urls)) for _, url := range urls { fn := func() error { body, err := requestBody(url); if (err != nil) { return err } // fmt.Println(body) mutex.Lock() bodies = append(bodies, body) mutex.UnLock() return nil } funcs = append(funcs, fn) } if err := awaitAll(funcs); err != nil { return nil, err } return bodies, nil } func main() { max := 4 urls := make([]string, 0, max) for i := 1; i <= max; i++ { url := createUrl(i) urls = append(urls, url) } fmt.Println("serial") startTime := time.Now() serial(urls) fmt.Println(time.Now().Sub(startTime)) fmt.Println("parallel") startTime = time.Now() parallel(urls) fmt.Println(time.Now().Sub(startTime)) }
動かしてみる
$ go version go version go1.9.2 linux/amd64 $ go get golang.org/x/sync/errgroup $ go run concurrent.go serial 11.157001925s parallel 4.398581223s
ちゃんと並列で実行されてる。