Go での非同期処理 その１

Go での非同期処理がいまいちわかっていなかったので、調べてみた。 Go の並列処理の基本は、「メモリをシェアして、コミュニケーションをとるのではなく、コミュニケーションをとって、メモリをシェアする」と言うコンセプトらしい。一瞬なんのことかわからないが、試してみよう。

# 1. 非同期処理を、複数は知らせて、全部終わったら、何らかの処理をする。

C# や TypeScript だと、 async/await が便利すぎていい感じだが、Go には go routine と、 channel が存在する。かなりかっこいい感じで並列処理がかける。 REST-API からデータを取ってきたかったり、IO関係の処理だと、並列処理を行いたいだろう。次のは、２つの web サーバーから並列処理で、データを取得して、両方が読み終わったら、内容を表示するサンプルだ。

```go
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func GetContent(url string, c chan string) {
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	s := string(body[:])

	if err != nil {
		fmt.Println(err)
		return
	}
	c <- s
}

func main() {
	fmt.Println("Hello, playground")
	c := make(chan string)
	go GetContent("https://www.bing.com/", c)
	go GetContent("https://www.yahoo.co.jp", c)
	result01, result02 := <-c, <-c
	fmt.Println(result01)
	fmt.Println("********-----------")
	fmt.Println(result02)

}
```
余談だが、`:` は、スライス 参考：　[Go Slices: usage and internals](https://blog.golang.org/go-slices-usage-and-internals)に使われる記号で、 `body[:]` は、スライスの前後が省略されているので、`byte[] body` の body のストレージ自体を指し示している。（それを string に変換している) 

ポイントを解説していこう。

## 1.1. Channel を作成する

非同期で実行されるメソッドは先頭に、`go` をつけて関数をコールする。これを`go routine` と呼ぶ。

```go
go GetContent("https://www.bing.com/", c)
```
ポイントは、下記の通り、channel と呼ばれるもので、メインの処理と、非同期に実行する処理で、シェアされるチャネルだ。メイン処理と、非同期処理の方で何か共有したかったら、チャネルを介して、データをやり取りする。この辺りが、「メモリをシェアして、コミュニケーションをとるのではなく、コミュニケーションをとって、メモリをシェアする」と書いてある所以だろう。下記のは、バッファーのないチャネルを作成しているが、バッファー付きも存在する。その違いはあとで解説する。

```go 
c := make(chan string)
```

## 1.2. 非同期処理側で、チャネルに書き込む

```go
func GetContent(url string, c chan string) {
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	s := string(body[:])

	if err != nil {
		fmt.Println(err)
		return
	}
	c <- s
}
```

前半は、引数でもらった、URL から、その　Web ページを取ってきて、それを`s` に代入しているだけだが、その戻り値を、channel に格納している。 `c <- s` の箇所である。こうすると、結果が、チャネルに格納されて、メイン側から、その値にアクセスできるようになる。

```go
	go GetContent("https://www.bing.com/", c)
	go GetContent("https://www.yahoo.co.jp", c)
	result01, result02 := <-c, <-c
```

メイン側では、２つの `go routine` を実行しているが、それぞれ、処理が始まったら、次の処理を待ち合わせない。じゃあ、`await`に相当するのはどうするかというと、３行目でやっている内容だ。

```go
result01, result03 := <-c, <-c
```

`<-c` を実行すると、チャネルから１つの値を取ってくる。つまり、この行を終えようとしたら、チャネルから、２つの値を取ってこないといけない。だから、ここで待ち合わせがかかるのだ。もし、`go GetContent("https://www.yahoo.co.jp", c)` の間にロジックがあったとしても、この行までは、待ち合わせは行わない。

go では、リソースをロックする方法ではなく、[Mutex](https://en.wikipedia.org/wiki/Mutual_exclusion) と言う方法で、データを共有する。Mutexの方法は、複数の人がいるときに、おもちゃのアヒルを持っている人だけが、話をできると言うルールにしておく。アヒルを持っている人でないと話はできない。参加者に次々アヒルを渡していけば、一人だけが話をするという状態になる。
　人をスレッドに置き換えると、アヒルが、Mutex だ。つまり、Channel そのものである。（たぶんw）　[What is a mutex?](https://stackoverflow.com/questions/34524/what-is-a-mutex)

このようにすると、実際に、２つの非同期処理が同時に実行されて、上記の箇所で待ち合わせて、プログラムが終了する。まさに、async, await っぽいことができている。ただ、考え方は違うので慣れが必要だ。

# 2. バッファー付きチャネルと、バッファーなしチャネルを理解する

ちなみに、チャネルには、バッファー付きと、バッファーなしが存在する。この違いをプログラムを作って理解してみよう。

```go

package main

import (
	"fmt"
	"time"
)

func GetInt(a int, c chan int) {
	for i := 1; i < 5; i++ {

		c <- a + i
		fmt.Println("GetInt:", i)
	}
	close(c)
}

func main() {
	c := make(chan int)

	go GetInt(0, c)

	for d := range c {
		time.Sleep(time.Second * 2)
		fmt.Println("Main: ", d)
	}

}
```
## 2.1. バッッファーなし channel

このプログラムでは、`go routine` の方で、チャネルに、書き込み、メインの方では、２秒ほど待ってから、チャネルを読み込んでいる。これを実行するとどうなるだろう。チャネルにデータを入れたくても、バッファがないので、１つか入れれない。だから、メイン側で、読み込みが行われないと、`go routine` 側で、書き込みを行えない。だから、 `go routine` 側で１つデータが入ったら、メインで１つ読み込む、、、といった感じになる。

```
$ go run spike3.go
GetInt: 1
Main:  1
GetInt: 2
Main:  2
GetInt: 3
Main:  3
GetInt: 4
Main:  4

```

## 2.2. バッファ付き channel

じゃあ、バッファ付きだとどうなるだろう？ バッファが２つになる。

```go
c := make(chan int, 1)
```

実行すると、バッファが２つあるので、読み込みは、２件同時にできている。でも、読み込みが行われないと、バッファがあかないので、最初以降は、１件読み込まれたら、１件書き込みができると言うような形で動いている。

```
$ go run spike3.go
GetInt: 1
GetInt: 2
Main:  1
GetInt: 3
Main:  2
GetInt: 4
Main:  3
Main:  4
```
もっと豪勢にバッファを持ってみる。

```go
c := make(chan int, 3)
```

予想通り、一気にバッファに　ぶち込んで、一気に読み取ると言うことができている。

```
$ go run spike3.go
GetInt: 1
GetInt: 2
GetInt: 3
GetInt: 4
Main:  1
Main:  2
Main:  3
Main:  4

```

# 3. コンカレントと、パラレルの違い

並列処理の英語訳は何かわからないが、Concurrent と、 Parallel は意味が違う。

Stack Overflow のこの図が最高にわかりやすい。
[What is the difference between concurrent programming and parallel programming?](https://stackoverflow.com/questions/1897993/what-is-the-difference-between-concurrent-programming-and-parallel-programming)
![con_and_par.jpg](https://qiita-image-store.s3.amazonaws.com/0/3470/4c9caacf-717e-ebc9-037c-db99bba94f2f.jpeg)

`go routine` は、コンカレントプログラミングを実施するものなので、パラレル実行のためには、一工夫必要だ。

さっきのプログラムを改造してみる。

すごく単純だが、CPU の数を取ってきて、その数だけ、Channelのバッファを作ればいい。

```go
package main

import (
	"fmt"
	"runtime"
)

func GetInt(a int, c chan int) {
	c <- a
	fmt.Println("GetInt", a)
}

func main() {
	numCPU := runtime.GOMAXPROCS(0)
	fmt.Println("NUMCPU:", numCPU)
	c := make(chan int, numCPU)

	for i := 0; i < numCPU-1; i++ {
		go GetInt(i*10, c)
	}
	result03, result02, result01 := <-c, <-c, <-c
	fmt.Println("Main:", result01)
	fmt.Println("Main:", result02)
	fmt.Println("Main:", result03)
}
```
実行結果

```
$ go run spike3.go
NUMCPU: 4
GetInt 20
GetInt 10
GetInt 0
Main: 10
Main: 0
Main: 20
```

多少順番は入れ替わっているが、予想通りの結果になっている。オリジナルのコードでは、CPUの数だけ回していたが、メインが動いているCPUがあるはずなので、３つにしてみた。

# 終わりに

`go routine` の強力さはわかってきたが、まだまだ、`go routine`で `panic` 発生したらどうやってデバッグするんだろう？とかわかっていないこともあるので、次回以降掘り下げたい。

## リファレンス

* [Effective Go](https://golang.org/doc/effective_go.html#concurrency)
* [Go Slices: usage and internals](https://blog.golang.org/go-slices-usage-and-internals)
* [What is the difference between concurrent programming and parallel programming?](https://stackoverflow.com/questions/1897993/what-is-the-difference-between-concurrent-programming-and-parallel-programming)
* [What is a mutex?](https://stackoverflow.com/questions/34524/what-is-a-mutex)
* [Unbuffered and buffered channels](https://nanxiao.gitbooks.io/golang-101-hacks/content/posts/unbuffered-and-buffered-channels.html)