LoginSignup
1
0

More than 5 years have passed since last update.

goroutineのサンプルを色々書いてみた

Last updated at Posted at 2017-10-16

はじめに

こちらの記事で
GoでLTやることになったので準備用のメモ
GoのLTのメモを書きましたが、

Goの良いところの1つである
goroutine(ゴルーチン)について書けなかったので追加記事を書いておきます。

通常バージョンから
読み書き(push・pull)並行バージョン。
更に、pushも並行でpullも並行バージョン。
などなど
色んなサンプルを書いてみます。


goroutineとは

簡単に並行処理をさせることができるGoの機能。
並行処理ができるので当然早い。


何よりもサンプル

まずは普通のfor文。

func main() {
    number := []int{1, 2, 3, 4, 5}
    serial(number)
}

func serial(number []int) {
    for _, n := range number {
        fmt.Println("serial: ", n)
    }
}
serial:  1
serial:  2
serial:  3
serial:  4
serial:  5

goroutineで並行処理

並行処理させたいところで go func をするだけ。
これから並行処理ができてしまう。

func main() {
    number := []int{1, 2, 3, 4, 5}
    parallel(number)
}

func parallel(number []int) {
    for _, n := range number {
        go func(n int) {
            fmt.Println("parallel: ", n)
        }(n)
    }
}
結果なし

なぜ結果なし!?

それは簡単で
並行処理になるので Print される前にプロセスが終わってしまうから。


とりあえず待たせてみる

最後に 1秒sleep を追加。

func parallel(number []int) {
    for _, n := range number {
        go func(n int) {
            fmt.Println("parallel: ", n)
        }(n)
    }
    time.Sleep(time.Second)
}
parallel:  2
parallel:  3
parallel:  1
parallel:  5
parallel:  4

並行処理になるので、1~5がバラバラに表示される。


何秒待てば良いの?

sleep するだけでは待ち時間が分からないと思うので
きちんとした待ち制御を入れるには sync を使う。

func parallel_wg(number []int) {
    var wg sync.WaitGroup
    for _, n := range number {
        wg.Add(1)
        go func(n int) {
            fmt.Println("parallel_wg: ", n)
            wg.Done()
        }(n)
    }
    wg.Wait()
}
parallel_wg:  5
parallel_wg:  1
parallel_wg:  2
parallel_wg:  3
parallel_wg:  4

これでOK。


readとwriteを並行にすることもできる

その場合には channel を使う。
pushされたものからpullできる。

func parallel_channel(number []int) {
    res := make(chan int, len(number))
    go func() {
        defer close(res)
        for _, n := range number {
            fmt.Println("parallel_channel push: ", n)
            res <- n
            time.Sleep(time.Millisecond)
        }
    }()

    for r := range res {
        fmt.Println("parallel_channel pull: ", r)
    }
}
parallel_channel push:  1
parallel_channel pull:  1
parallel_channel push:  2
parallel_channel pull:  2
parallel_channel push:  3
parallel_channel pull:  3
parallel_channel push:  4
parallel_channel pull:  4
parallel_channel push:  5
parallel_channel pull:  5

応用編

pushも並行。pullも並行。

func parallel_channel_wg(number []int) {
    var wg sync.WaitGroup
    res := make(chan int, len(number))
    go func() {
        defer close(res)
        for _, n := range number {
            wg.Add(1)
            go func(n int) {
                fmt.Println("parallel_channel_wg push: ", n)
                res <- n
                wg.Done()
            }(n)
        }
        wg.Wait()
    }()

    for r := range res {
        fmt.Println("parallel_channel_wg pull: ", r)
    }
}
parallel_channel_wg push:  4
parallel_channel_wg push:  2
parallel_channel_wg push:  1
parallel_channel_wg pull:  4
parallel_channel_wg pull:  2
parallel_channel_wg pull:  1
parallel_channel_wg push:  3
parallel_channel_wg pull:  3
parallel_channel_wg push:  5
parallel_channel_wg pull:  5

色々できます

javascriptでいうところの promise.all のようなことも簡単にできるということです。

goroutineはCPUやコア数に応じて使う数も指定できたり
やりたいことに合わせて設定できます。

ただし、勝手にスレッドセーフになる訳ではないので
goroutine内で使うデータについては注意が必要です。
 ⇒【2017/10/17 追記】
  Goのバージョン1.9からは sync.Map というものがあるので排他制御もお任せで大丈夫なようです!


ということで

便利なgoroutineのお話でした。

前回の記事 もそうですけど
やっぱGoって何でも簡単にできちゃうので楽で良いですね。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0