Help us understand the problem. What is going on with this article?

goの並列処理入門編 goroutineとchannel

ReadMeFirst

最初、Context周りを色々調べた結果を記事にしていたのですが、
前提で並列処理がイメージつかないとアレかなぁ
と思ったので、goの並列処理について簡単にパパッとまとめておきます。

Goの並列処理はカジュアルなので、世に記事が溢れてるのはご愛嬌で。

概念 (goroutine and channel)

Goの並行処理を構成する2つの要素について簡単に。

goroutine

Goのランタイムによって管理される軽量スレッド。
関数の呼び出しに go をつけるだけで立ち上げることができる。
go Doanything(ctx) みたいな感じ。

channel

ざっくりリファレンスから抜粋。

並列実行された関数間における値の送受信を提供する型です。
宣言はchannelで扱う値の型と共に行います。
送信・受信それぞれの専用とすることも可能です。

chan T  // T型の送信・受信 を宣言
chan <- float64 //  float64の送信専用
<- chan int // intの受信専用

chan bool<- chan bool は別の型になりますので、ビルド時にエラーになる。
cannot use sig (type chan<- bool) as type chan bool

初期化はmakeで。第2引数はbufferの最大長です。

make(chan int,100) 

専用のオペレーターを使って値の送受信ができる。

  • 値の送信は channel <- 値
  • 値の受信は <-channel
  • len(channel)で現在格納されている数を計る事もできる(buffer指定しないと0のまま)

channelの値は格納した順に取り出されます。

buffer数値はdefaultは0ですが、>0の値を設定した場合、
最大長以上の書き込みをBlock(待ち)する事を利用して同時実行数を
制限するような使い方もあります。

close(channel)でchannelを閉じる事ができます。

簡単な例

Sample

package main

import (
    "fmt"
    "time"
)

func main() {
    const totalExecuteNum = 6   //合計実行数
    const maxConcurrencyNum = 3 //同時実行数
    sig := make(chan string, maxConcurrencyNum)
    res := make(chan string, totalExecuteNum)
    defer close(sig)
    defer close(res)
    fmt.Printf("start concurrency execute %s \n", time.Now())
    for i := 0; i < totalExecuteNum; i++ {
        go wait6Sec(sig, res, fmt.Sprintf("no%d", i))
    }
    for {
        //全部が終わるまで待つ
        if len(res) >= totalExecuteNum {
            break
        }
    }

    fmt.Printf("end concurrency execute %s \n", time.Now())
}

func wait6Sec(sig chan string, res chan string, name string) {
    sig <- fmt.Sprintf("sig %s", name)
    time.Sleep(6 * time.Second)
    fmt.Printf("%s:end wait 6sec \n", name)
    res <- fmt.Sprintf("sig %s", name)
    <-sig
}

実行結果

go run main5.go
start concurrency execute 2017-04-19 18:54:43.276968001 +0900 JST
no2:end wait 6sec
no3:end wait 6sec
no1:end wait 6sec
no5:end wait 6sec
no4:end wait 6sec
no0:end wait 6sec
end concurrency execute 2017-04-19 18:54:55.279826923 +0900 JST

channelのbuffer数を利用して同時実行数を制限してみました。
(合計回数(6) / 同時実行数(3)) * 6 = 12秒程度で終わってますね :)

IMO

カジュアルに使えるので、積極的に使うべきではあるけど、
基本的に並列処理自体、直列の処理より頭のリソースを使うし、
複数人開発で無秩序に色んなところで使われると、goroutineがバカスカ立ち上がったり(queueingで対策すれば良いけど)など
注意するべき所もあります:P
適用部分がサービスにとって、とにかく速度が大事な部分かどうかと言う、
複雑さと速度改善は天秤にかけるべきかな、と。

補足

contextの記事書く前に補足記事的に書いたのですが、
実際には、mattnさんが紹介してるsync package
などもあるので、見てみると良いのかな〜と思います:)

参考

Channel types
Tour of Go
Big Sky

marnie_ms4
https://twitter.com/marnie0301 ゆるふわWeb・iOSエンジニア golang,PHP,Java,Objective-C,Javascript,Rubyなど swiftは業務で使うことはなかった...
https://twitter.com/marnie0301
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした