goroutineとチャネルの動きを図を使って理解する(和訳)

# はじめに
先週の[Golang Weekly](https://golangweekly.com/)で流れてきた下記の記事が、`groutine`と`チャネル`の動きについてわかりやすそうだったので英語の勉強も兼ねて和訳してみました。
変な訳し方もあったら指摘いただけると幸いです。
[A visual introduction to golang concurrency and goroutines](https://stackedco.de/a-visual-introduction-to-golang-goroutines)
`和訳中に利用する画像やコードは全て上記からの引用です。`

# A visual introduction to golang concurrency and goroutines
Golangは、goroutineを利用した驚くべき並行(≠並列)システムとして知られている言語である。
Golangはgoroutineとチャネルによって平行性を実現している。

## Prerequisites
- go構文の基本的な理解

## What are Goroutines?
goroutineはスレッドとして考えることもできるが、異なる点がいくつかある。

- goroutineは、平均8kB軽い
- goroutineは、チャネルと通信するが、スレッド通信の方が複雑
- goroutineのスケジューリングは、実行時にgoによって管理されている
- goroutineには、アイデンティティがない
- goroutineは、実際のOSスレッド上で実行され、その数は`GOMAXPROCS`によりコントロールされている

## Goroutine states
このチュートリアルでは、goroutineが次のうちの1つの状態に常にいる状態と考えると便利である。
[![goroutineの状態図](https://www.datocms-assets.com/11259/1559300683-image3.png)](https://www.datocms-assets.com/11259/1559300683-image3.png)

- Waiting: goroutineは、ブロッキングステートメントにブロックされていないが、スケジューラによってまだ実行されていない状態。
- Running: goroutineは、スケジューラによって実行されている状態。
- Blocking: goroutineは、ブロッキング操作(チャネルやスリープ、ネットワーク操作等)によってブロックされている。そしてスケジューラは、次に利用可能なgoroutineへと遷移する。
- Executed: goroutineは、完全に実行され、終了する。

## Firing a goroutine
このコードを見てください。

```go
func main(){
    s := "main"
    go side()
    for _,item := range s{
        fmt.Println(string(item))
    }
}

func side(){
    s := "goroutine"
    for _,item := range s{
        fmt.Println(item)
    }
}
```

```bash:実行結果
main
```
##### 何が起きているか？
[![goroutineの状態図](https://www.datocms-assets.com/11259/1559299404-imaage2.png)](https://www.datocms-assets.com/11259/1559299404-imaage2.png)
`side()`がgoroutineとして実行するために呼ばれているが、まだそこからの出力は受け取っていない。なぜか？
`goroutine()`はまだready状態ではなく、スケジューラがアプリケーションの実行を通して実行しなかった。

もしこのように変えると:

```go
...
func main(){
    s := "main"
    go side()
    for _,item := range s{
        fmt.Println(string(item))
    }
    time.Sleep(time.Millisecond)
}
...
```

##### これが起きる
[![goroutineの状態図](https://www.datocms-assets.com/11259/1559299418-image1.png)](https://www.datocms-assets.com/11259/1559299418-image1.png)
メインのgoroutineでブロッキングステートメントである`time.Sleep`が呼ばれ、goroutineが`blocked`状態になる。
それはgoスケジューラが、実行する準備ができている状態のスケジュールされたgoroutineを実行することを可能にする。この場合は、`goroutine()`が実行される。

これから、スケジュールされるまでgoがgoroutineを実行しないことがわかる。
そして、どのようにgoroutineを実行するのか？他の全てのgoroutineを使うことで、それを実行する、もしくはブロックする前に並べられる。

## Multiple goroutines.
下記のコード考える:

```go
func main(){
    go sayWord("goroutine1")
    go sayWord("goroutine2")
    sayWord("Main")
    time.Sleep(time.Millisecond)
    fmt.Println("End of main")
}

func sayWord(word string){
    for i := 0;i< 2; i++ {
        fmt.Println(word)
    }
}
```

```bash:実行結果
Main
Main
goroutine1
goroutine1
goroutine2
goroutine2
End of main
```
これが起こっている:
[![goroutineの状態図](https://www.datocms-assets.com/11259/1559346076-webp-net-resizeimage-1.png)](https://www.datocms-assets.com/11259/1559346076-webp-net-resizeimage-1.png)

## Channels Intro
goroutine間のパイプラインとしてチャネルを考える。チャネルは、(**1種類の**)データを送信でき、そこから読み取ることもできる。
> このチュートリアルでは緑矢印がチャネルを表している

## Declaring a channel
チャネルは次の構文で生成できる。

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

これでデータを送受信できる。

```go
//Send hello to it
pipe <- "Hello"
go goroutine(pipe)
//Receive hello from it
func goroutine(pipe chan string){
 hello := <- pipe   
}
```

## Unidirectional Channel
初期化時の矢印の方向で、チャネルをインプットもしくはアウトプット専用としても使うことができる。

```go
func goroutine(in <-chan string, out chan<- string){
    received := <-in
    out <- "processed"
}
```

### Channels are blocking by default.
データに`channel <- "Hi"`が書きこまれたとき、goroutineは他のgroutineがそのチャネルから読み込むまでブロックされる。

> NOTE: もし他のgoroutineが読み込まなければ、デッドロックが起きてプログラムがクラッシュする。

同様に、もし何もないのにチャネルからデータを読み込もうとしたら、goroutineは他のgoroutineがチャネルにデータを送信するまでブロックされる。

下記のコードを考えてみる:

```go
func main() {
    pipe := make(chan string)
    go sayWord(pipe)
    fmt.Println("start of main")
    pipe <- "Hello"
    fmt.Println("End of main")

}

func sayWord(in <-chan string) {
    fmt.Println(<-in)
    fmt.Println("Data received in sayWord")
}
```

```go:実行結果
start of main
Hello
Data received in sayWord
End of main
```

これが起きている:
[![goroutineの状態図](https://www.datocms-assets.com/11259/1559363102-webp-net-resizeimage.png)](https://www.datocms-assets.com/11259/1559363102-webp-net-resizeimage.png)

- `main`goroutineは、データがバッファがない状態のパイプチャネルに送信されるまで、実行する。(ブロッキング処理)
- `sayword`はスケジューラによって実行され、一度`main`を読んでいるデータがレディ状態に遷移する。
- `main`はその実行を完了する。

## Buffered channels
バッファのあるチャネルは`make()`コマンドの第2引数でバッファサイズを指定することで作成される。

バッファのあるチャネルとないチャネルの違いは、下記を含んでいる。

- バッファのあるチャネルにデータをプッシュしても、データが一杯になるまではgoroutineがブロックされない。
- バッファのないチャネルからのデータ読み込みは、空になるまではノンブロッキング状態となる。

> バッファのないチャネルは`make(chan T, 0)`と同じものと考えられる。

#### Sample code

```go
func main() {
    pipe := make(chan int,3)
    go consoleLog(pipe)
    for i := 0; i < 3; i ++{
        pipe <- i
    }
    fmt.Println("Done")
}

func consoleLog(in <-chan int) {
    //Infinite loop to always read channel when running
    for{
        fmt.Println(strconv.Itoa(<-in))
    }
}
```

```go:実行結果
Done
```

見たとおり、goroutineは実行されない。なぜなら、`main`関数のforループで3つの値がcapが3のバッファに追加されるだけで、`main`のgoroutineはブロックされること無く実行されるため。

4つの値を追加した場合は:

```go
func main() {
    pipe := make(chan int,3)
    go consoleLog(pipe)
    for i := 0; i < 4; i ++{
        pipe <- i
    }
    fmt.Println("Done")
}
...
```

```go:実行結果
0
1
2
3
Done
```

- `main`goroutineは4回目のチャネルへのプッシュでブロックされる。
- コントロールは、チャネルを空にする無限ループを実行する`consoleLog`goroutineに与えられる。
- `consoleLog`goroutineは、空のチャネルから読み込もうとするとブロックする。
- コントロールは、`main`に戻り、残りが実行されて終了する。

### Range

`range`句はさっきの無限ループの代わりに、下記コードのように使うことができる。

```go
func sayWord(in <-chan int) {
    //Infinite loop to always read channel when running
    for item := range in{
        fmt.Println(strconv.Itoa(item))
    }
}
```

結果はさっきと同じものが出力される。

## Select

`select`は、チャネルの`switch`と似ている。`select`は下記を行う。

- 最初のノンブロッキングのものを実行する。
- もし1つ以上がノンブロッキングであれば、ランダムに1つを実行する。
- 全てがブロックされている場合、いずれかが解除されるまで全てをブロックする。`default`がない限りは。

```go
func main() {
    pipe := make(chan string, 2)
    second := make(chan string, 2)
    go goroutines(second, time.Second*3,"First goroutine")
    go goroutines(pipe, time.Second*2,"Second goroutine")

    select {
    case res := <-pipe:
        fmt.Println(res)
    case res := <-second:
        fmt.Println(res)
    }
}

func goroutines(out chan<- string, wait time.Duration, name string) {
    time.Sleep(wait)
    out <- name
}
```

```bash:実行結果
Second goroutine
```

起きていることをブレークダウンすると:
[![goroutineの状態図](https://www.datocms-assets.com/11259/1559369153-webp-net-resizeimage-1.png)](https://www.datocms-assets.com/11259/1559369153-webp-net-resizeimage-1.png)

### Default case

もし、上のコードを下記の通り更新すると、

```go
func main() {
    pipe := make(chan string, 2)
    second := make(chan string, 2)
    go goroutines(second, time.Second*3,"First goroutine")
    go goroutines(pipe, time.Second*2,"Second goroutine")

    select {
    case res := <-pipe:
        fmt.Println(res)
    case res := <-second:
        fmt.Println(res)
    default:
        fmt.Println("None are ready")
    }
}
```
実行結果は、`None are ready`となるはず。

利用可能な`default`がなければ、(もし全てがブロックされていたら)`select`はブロックする。`default`があれば、それがすぐに実行される。

## Conclusion.

この記事は、goroutineや、goroutineの通信、それらがどのようにスケジュールされているかを理解するのに役立つだろう。goroutineがチャネルをどのように上手く調整するかを理解できるように、チャネルの内部の動きに関するフォローアップ記事を書く予定である。(どう訳すかわからん...)

それまで、goroutineを試したり遊んだりして、異なる結果を出してみよう。もしくは`GOMAXPROCS`をCPUで利用可能なコア数に設定して、並列処理を試してみよう(注意が必要)。

# おわりに

上手く訳せなかったところも多く、これからの英語の勉強も必須だなと感じました。
内容自体は、コードや図もあるのでわかりやすく、理解できたと思います。
今までこうした動きは、何となくこう動いているんだろうなとしか考えたことがなかったので新鮮です。
こういった内部ロジックを理解することは、パフォーマンスにも影響していくので、引き続き勉強していきたいです。