LoginSignup
94
63

More than 5 years have passed since last update.

Go言語による並行処理備忘録 ~goroutine基礎編~

Posted at

はじめに

「Go言語による並行処理」を読んでいます。
しかし、なかなか難しいので自分用のメモとしてアウトプットしていきます。

説明形式で記事を書きますので、これからGo言語の並行処理をやる人は参考になるかもしれません。

並行処理とは?

Go言語の並行処理プログラミング

並行処理プログラミングには、一般的に大きく二つの方法があります。
・Shared-memory communication
 メモリを共有する事で通信をやりとりする事

・Message-passing communication
 通信によって(仲介業者を通して)メモリをやりとりする事

The Go Blog引用

Do not communicate by sharing memory; instead, share memory by communicating.

Go言語ではMessage-passing communicationを推奨しているそうです。

並列処理と並行処理の違いとは?

・Concurrent(並行)
 複数の動作が、論理的に、順不同もしくは同時に起こりうること

・Parallel(並列)
 複数の動作が、物理的に、同時に起こること
 
工場で10人メンバーが仕事をしているとしたら。。。
10人が同時に同じ木を切っている状態。これは並列処理です。
1人は木を切って、もう一人は金槌を叩いている、そしてもう一人は・・・。これは並行処理です。

そしてGo言語ではConcurrent(並行)を採用しているそうです。

※詳細なイメージは以下のサイトが役に立ちました。
parallel と concurrent、並列と並行の違い

「なんとなく」で終わらせてませんか?"いらすと"で覚える並列と並行の違い

放浪エンジニア K-site

Goroutine詳細

Goroutineとは?

Goroutineとは、一言で言うと「並行に動作している関数のこと」。
一番最初に呼び出されるfunc main(){}main Goroutineと呼ばれるものです。

func main() {
    go hoge()
    go fuga()
    go piyo()
}

こちらのコードを説明します。
まずはメインGoroutineが呼ばれます。
そしてhoge,fuga,piyoのGoroutineが並行で呼ばれています。
このコードからわかるようにmain Goroutine以外のGoroutineの呼び出し方は関数にgoを付けるだけです。

Goroutineの挙動

func main()  {
    fmt.Println("main start")
    go Greet(1)
    go Greet(2)
    go Greet(3)
    // time.Sleep(time.Second) ←追加したらちゃんと動く
    fmt.Println("main end")
}

func Greet(i int) {
    fmt.Println("hello",i)
}
// main start
// hello 3
// hello 2
// hello 1
// main end

time.Sleepが存在しない時は、main start と main endのみが表示されます。
各々のGoroutineはプロセスが終了するまで存在し続けます。
そしてmain Goroutineが終了した時に全体のプロセスが終了します。

つまり、main start と main endしか表示されなかった理由は各Goroutineより先にmain Goroutineが終了したことがわかります。
なのでtime.Sleepを使いmain Goroutineの終了を待つことによって各Goroutineの処理が先に行われて正しく動きました。
※Goroutineは順不同なので(並行だから)こういった1秒止めるなどはNGです。

GoroutineとShared Memory

func main()  {
    fmt.Println("main start")
    for i := 0; i < 5; i++ {
        go func() {
            // iを参照し続ける
            Greet(i)
        }()
    }
    time.Sleep(time.Second)
    fmt.Println("main end")
}

func Greet(i int) {
    fmt.Println("hello",i)
}
// main start
// hello 5
// hello 5
// hello 5
// hello 5
// hello 5
// main end

・Shared-memory・・・メモリを共有して通信をやりとりする事
・Message-passing・・通信によって(第三者を通して)メモリをやりとりする事
と上記で学びました。

各GoroutineがShared-memoryを使用すると、このような動作になります。
これはゴルーチンがスケジュールされた時のi値を参照見るので最後に代入された5をみています。

参照ではなくコピーを渡すことで回避できます。

go func(i int) {
    // iを参照し続ける
    Greet(i)
}(i)

こういったメモリの事を考えながら実装するのはとても難しいです。
なのでShared-memoryではなくMessage-passing(仲介業者を通して)の仕組みで実装していく必要があります。

ここで言う第三者とはchannelの事です。

最後に

goroutineについてすごく基礎的なところをまとめてみました。

次回はチャネルについてまとめていきます。

94
63
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
94
63