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

# はじめに
こちらの記事で
[GoでLTやることになったので準備用のメモ](https://qiita.com/courage-liberty/items/4ad979486f2d1f0cfbe8)
GoのLTのメモを書きましたが、

Goの良いところの１つである
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のお話でした。

[前回の記事](https://qiita.com/courage-liberty/items/4ad979486f2d1f0cfbe8) もそうですけど
やっぱGoって何でも簡単にできちゃうので楽で良いですね。