【Go】基本文法メモ⑦(並行処理)

# 概要
[A Tour of Go](https://tour.golang.org/)の順番に沿ってGoの基本で個人的に学習したことをまとめています。


| No | 記事 |
|------|---------|
|    1  |   [【Go】基本文法①(基礎)](https://qiita.com/k-penguin-sato/items/1d0e1c6b4bf937996cd3)|
|    2  |   [【Go】基本文法②(フロー制御文)](https://qiita.com/k-penguin-sato/items/31b45fd3914797b654f0)|
|    3  |  [【Go】基本文法③(ポインタ・構造体)](https://qiita.com/k-penguin-sato/items/62dfe0f93f56e4bf9157)|
|    4  |  [【Go】基本文法④(配列・スライス)](https://qiita.com/k-penguin-sato/items/daad9986d6c42bdcde90)|
|    5  |   [【Go】基本文法⑤(Maps・ Range)](https://qiita.com/k-penguin-sato/items/a320072fa09502bde3e9)|
|    6  |    [【Go】基本文法⑥(インターフェース)](https://qiita.com/k-penguin-sato/items/885a61d819cc431304f5)     |
|    7  | 〜〜 __【Go】基本文法⑦(並行処理)「今ココ」__〜〜|
|    8  |    [【Go】基本文法総まとめ](https://qiita.com/k-penguin-sato/items/deaeab18aa416496e273)     |

今回は `Goroutine(ゴルーチン)` と `Channel` に焦点を当てて学習しました。

# goroutine(ゴルーチン)とは
`goroutine(ゴルーチン)`とは、Go言語のプログラムで並行に実行されるもののことです。

# goroutine(ゴルーチン)の起動
Goの場合、関数 (またはメソッド) の呼び出しの前に `go` を付けると、異なる `goroutine` で関数を実行することができます。

```Golang
go 関数名(引数, ...)
```
`go`は新しい`goroutine`を生成して、その`goroutine`内で指定された関数を実行します。
新しい`goroutine`は並行に動作するので、関数の実行終了を待つことなく、`go`のあとに記述されているプログラムを実行します。

# goroutine(ゴルーチン)の終了条件
`goroutine(ゴルーチン)`は下記の条件で終了します。

- 関数の処理が終わる。
- `return`で抜ける。
- `runtime.Goexit()` を実行する

# 存在するgoroutine(ゴルーチン)の数の取得方法
`runtime.NumGoroutine()`を使用する事で現在起動している`goroutine(ゴルーチン)`の数を知ることができます。

```Golang
import( 
 "fmt"
 "log"
 "runtime"
)

func main() {
     log.Println(runtime.NumGoroutine())
}
```

# goroutine(ゴルーチン)の実例
実際のコードを見る事でより`goroutine(ゴルーチン)`を具体的に捉えます。
以下のプログラムは1秒間隔で`str`を`num`回表示します。

```Golang
package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("Start!")
	 process(2,"A")
	 process(2,"B")
	fmt.Println("Finish!")
}

func process(num int, str string) {
	for i := 0; i <= num; i++ {
	 time.Sleep(1 * time.Second)
	 fmt.Println(i, str)
	}
}
```

この例では`goroutine(ゴルーチン)`も何も使用していないので実行結果は以下の様になります。

```
Start!
0 A
1 A
2 A
0 B
1 B
2 B
Finish!
```

そこで、新しい`goroutine`を2つ生成して関数`process()`を実行します

```Golang
func main() {
	fmt.Println("Start!")
	go process(2,"A") //goキーワードで関数実行するとgoroutineが生成される
	go process(2,"B")
	fmt.Println("Finish!")
}

func process(num int, str string) {
	for i := 0; i <= num; i++ {
	 time.Sleep(1 * time.Second)
	 fmt.Println(i, str)
	}
}
```

すると実行結果は以下の様になりました。

```
Start!
Finish!
```

作成された`goroutine`の終了を待たず`main`が終了しプログラム全体の処理を終了したため、 関数`process()`の実行結果は得られませんでした。
関数`process()`の実行結果を得るためには、`main`が`goroutine`が終了するまで待たなければいけません。
これは`channel(チャネル)`を使う事で簡単に実現できます。

# channel(チャネル)とは
ゴルーチン間(main含む)で連携するには、`channel(チャネル)`と呼ばれる機能を利用します。
`channel(チャネル)`は、値の交換および同期という通信機能を兼ね備えており、2つの計算処理(ゴルーチン)が予期しない状態とならないことを保証します。

# channel(チャネル)の生成方法
チャネルは Go 言語に標準で用意されているデータの一つで、スライスと同じタイプのデータ構造です
スライスの様に組み込み関数 `make()`を使用する事で生成できます。

```Golang
ch := make(chan 型)
ch := make(chan 型, バッファサイズ)
```
チャネルに指定する`バッファサイズ`は、チャネルにバッファ可能な容量です。このサイズが送信データの上限となります。

#channel(チャネル)での値の送受信方法
`channel(チャネル)`を使用するには、チャネル型の変数を作成し、送信側・受信側ともに、その変数に対して何らかのデータを送受信します。
以下の例の様にチャネルオペレータの `<-` を用いる事で値の送受信ができます。

```Golang
ch <- data    //dataをchへ送信する(vをchに書き込む)
arg := <-ch  //chから受信した変数をargへ割り当てる(chの値を読み込む)
```
送信が`channel<-value`で受信が`<-channel`です。
以下のコードが`channel(チャネル)`での値の送受信の実例です。

```Golang
func main() {
	//channelの作成
    messages := make(chan string)
	
	//作成されたchannelに値(str)を送信
    go func() { messages <- "str" }()
	
	//channelから値を受信
    msg := <-messages
    fmt.Println(msg) //=> str
}
```

# ゴルーチンの同期
Goでは__受信側では常に、受信可能なデータが来るまでブロックされます。__
また、送信側はチャネルがバッファリングしていないときは、受信側が値を受信するまでブロックされます。
これにより、Goでは明確なロックや条件変数がなくても、goroutineの同期を可能にします。

### ゴルーチンの同期(例①)
いくつかの実例から上記の意味を理解します。

```Golang
func main() {
  ch := make(chan bool)  //bool型のchannelを作成

	// ゴルーチンとして以下の関数を起動。完了時にchannelの型であるboolの値を送信する事で、チャネルへ通知。
	go func() {
		fmt.Println("Hello")
		ch <- true  // 通知を送信。値は何でも良い(boolの型であれば)
	}()

  <-ch //=>Hello   
	// channelの型であるboolの値を受け取るまでの完了待ち。送られてきた値は破棄
}

```
上記ではbool型のchannel`ch`を作成し、ゴルーチンとして関数`func()`を起動しています。
`func()`内では`ch`の型であるboolの値を与えています。
Goでは受信側では常に受信可能なデータが来るまでブロックされるので、main内で`ch`の型であるboolの値を受け取るまで完了待ちしています。

### ゴルーチンの同期(例②)
次に以下の例もみてみます。

```Golang
func hello(done chan bool) {  
    fmt.Println("Hello world goroutine")
    done <- true
}

func main() {  
         //bool型のchannelのdoneを生成する。
    done := make(chan bool)
	
	//生成したdoneを関数helloに渡す
	go hello(done)
    
	<-done
	//main
    fmt.Println("main function")

} 
```
上記の例ではbool型のchannel`done`を作成し、関数`hello()`に渡しています。
これによって`main`内で`<-done`が呼ばれた際に、関数`hello()`でbool型の要素が渡されるまで完了待ちをしています。

### ゴルーチンの同期(例③)
ここで再びgoroutine(ゴルーチン)の実例で作成した、以下のコードについてみていきます。

```Golang
func main() {
    fmt.Println("Start!")
    go process(2,"A") //goキーワードで関数実行するとgoroutineが生成される
    go process(2,"B")
    fmt.Println("Finish!")
}

func process(num int, str string) {
    for i := 0; i <= num; i++ {
     time.Sleep(1 * time.Second)
     fmt.Println(i, str)
    }
}
```
現状では、作成されたgoroutineの終了を待たずmainが終了しプログラム全体の処理を終了したため、 関数process()の実行結果は得られませんでした。
これをchannelに関して学んだ知識を踏まえて以下の様に書き換えます。

```Golang
func main() {
	ch1 := make(chan bool)
	ch2 := make(chan bool)
   
	fmt.Println("Start!")
	
    go func() {
		process(2,"A")
    	ch1 <- true
    }()
	
	go func(){
		process(2, "B")
		ch2 <- true
	}()
	
	<-ch1
    <-ch2
	
    fmt.Println("Finish!")
}

func process(num int, str string) {
    for i := 0; i <= num; i++ {
     time.Sleep(1 * time.Second)
     fmt.Println(i, str)
    }
}
```
上記ではbool型の`channel`である`ch1`と`ch2`を作成し、`main`で`<-ch1`や`<-ch2`が呼ばれると、bool型の要素を受信するまで完了待ちをしています。
すると、関数内で`process()`が評価されるので、期待通りの実行結果を得る事ができました。

```
Start!
0 A
0 B
1 B
1 A
2 A
2 B
Finish!
```

# 参考
[My Journey of Go ⑦](https://k-blog0130.herokuapp.com/posts/21)
[Goの並列処理の動作を理解する](https://tikasan.hatenablog.com/entry/2017/05/20/170050)
[Go の並行処理](http://jxck.hatenablog.com/entry/20130414/1365960707)
[お気楽 Go 言語プログラミング入門](http://www.geocities.jp/m_hiroi/golang/abcgo12.html)
[Goの並行処理 -基本のキ-](https://blog.excite.co.jp/exdev/27147029/)
[Go by Example](https://gobyexample.com/channel-synchronization)
[An introduction to using and visualizing channels in Go](https://www.sohamkamani.com/blog/2017/08/24/golang-channels-explained/)
[GOLANGBOT.COM Part 22: Channels](https://golangbot.com/channels/)
[GoのChannelを使いこなせるようになるための手引](https://qiita.com/awakia/items/f8afa070c96d1c9a04c9)
[ゴルーチンと並行性パターン](https://qiita.com/hayajo/items/4cd75f87e35e60ae11a9)
[実践Go言語(part12)](http://golang.jp/2010/03/1260)