Go言語(golang)の、goroutineとchannelの処理を、コードから読み解く


```go
package main

import "fmt"

func doBusinessLogic(val int) int {
	// valを使って何かをする
	return val * val // 単純な例：自乗する
}

// liststart1
// ファイルch10/ex1000.go。 詳細は次の節以降で説明
func runThingsConcurrently(chIn <-chan int, chOut chan<- string) {
	for val := range chIn { // chInから値が到着するたびに
		go func(val int) { // 新たにゴルーチンを呼び出す
			result := doBusinessLogic(val) // valを処理して結果をresultに代入
			resultString := fmt.Sprintf("%d -> %d", val, result)
			chOut <- resultString // 結果をchOutに入れる
		}(val)
	}
} //listend1

func main() {
	chIn := make(chan int)     // intのチャネルを生成。送信用
	chOut := make(chan string) // stringのチャネルを生成。受信用
	go runThingsConcurrently(chIn, chOut)

	vals := []int{1, 2, 3, 4, 5}
	for v := range vals { // スライスvalsの各要素に対して
		chIn <- v // その値をchInに送信
	}

	i := 0
	for v := range chOut { // chOutから受信するごとに
		fmt.Println(v) // 結果を出力。順番は不定
		i++
		if len(vals) <= i { // 送った要素の数と同じだけ到着したら
			break // forループを抜ける
		}
	}

	close(chOut) // chOutをクローズ（終了してしまうので、なくても大丈夫）
	close(chIn)  // chOutをクローズ（同様）
}

```
```go
func doBusinessLogic(val int) int {
```
- `doBusinessLogic` という関数を定義します。引数として整数 `val` を受け取り、整数を返します。

```go
    return val*val   // 単純な例：自乗する
```
- `val` の自乗を計算し、その結果を返します。この関数はビジネスロジックを実行するための例です。

```go
func runThingsConcurrently(chIn <-chan int, chOut chan<- string) {
```
- `runThingsConcurrently` という関数を定義します。入力チャネル `chIn` から整数を受け取り、出力チャネル `chOut` に文字列を送信します。

```go
    for val := range chIn { // chInから値が到着するたびに
```
- `chIn` からの値を受信するためのループを開始します。`chIn` が閉じられるまで続きます。

```go
        go func(val int) {  // 新たにゴルーチンを呼び出す
```
- 受信した `val` を使って新しいゴルーチンを開始します。このゴルーチンは並行して実行されます。

```go
            result := doBusinessLogic(val) // valを処理して結果をresultに代入
```
- `doBusinessLogic` 関数を呼び出し、`val` を処理してその結果を `result` に代入します。

```go
            resultString := fmt.Sprintf("%d -> %d", val, result)
```
- `fmt.Sprintf` を使用して、`val` とその結果 `result` を文字列にフォーマットします。

```go
            chOut <- resultString // 結果をchOutに入れる
```
- フォーマットされた文字列を `chOut` に送信します。

```go
        }(val)
```
- 匿名関数に `val` を引数として渡し、即座に実行します。

```go
} //listend1
```
- `runThingsConcurrently` 関数の定義が終了します。

```go
func main() {
```
- `main` 関数の定義を開始します。プログラムのエントリーポイントです。

```go
    chIn := make(chan int)  // intのチャネルを生成。送信用
```
- 整数型の送信用チャネル `chIn` を作成します。

```go
    chOut := make(chan string) // stringのチャネルを生成。受信用
```
- 文字列型の受信用チャネル `chOut` を作成します。

```go
    go runThingsConcurrently(chIn, chOut)
```
- `runThingsConcurrently` 関数をゴルーチンとして実行します。これにより、非同期で値の処理が行われます。

```go
    vals := []int{1, 2, 3, 4, 5}
```
- 整数のスライス `vals` を定義します。このスライスには送信する値が格納されています。

```go
    for v := range vals { // スライスvalsの各要素に対して
```
- `vals` の各要素に対してループを開始します。

```go
        chIn <- v   // その値をchInに送信
```
- 現在の値 `v` を `chIn` に送信します。


```go
    i := 0
```
- 受信した値のカウント用の変数 `i` を初期化します。

```go
    for v := range chOut { // chOutから受信するごとに
```
- `chOut` からの値を受信するためのループを開始します。

```go
        fmt.Println(v) // 結果を出力。順番は不定
```
- 受信した値を出力します。順序は保証されません。

```go
        i++
```
- 受信カウントを1増やします。

```go
        if len(vals) <= i { // 送った要素の数と同じだけ到着したら
```
- 送り込んだ要素数と受信した要素数を比較します。

```go
            break  // forループを抜ける
```
- 受信した要素数が送り込んだ要素数と同じになった場合、ループを抜けます。


```go
    close(chOut) // chOutをクローズ（終了してしまうので、なくても大丈夫）
```
- `chOut` を閉じます。これにより、受信側はこれ以上の値が送信されないことを知ります。

```go
    close(chIn) // chOutをクローズ（同様）
```
- `chIn` を閉じます。これにより、送信側も終了します。


### ゴルーチン（goroutine）

- **定義**: ゴルーチンはGo言語における軽量スレッドです。`go`キーワードを使って関数を呼び出すことで、非同期に実行されます。

- **特徴**:
  - **軽量**: ゴルーチンはスレッドに比べて非常に軽量で、数万のゴルーチンを同時に実行することができます。
  - **スタック管理**: ゴルーチンは動的にスタックを管理するため、初期のスタックサイズは小さく、必要に応じて拡張されます。

- **並行処理**: ゴルーチンを使用することで、プログラム内で複数の処理を並行して実行できます。これにより、I/O待ちや計算処理を効率的に行うことが可能です。

#### コード内のゴルーチンの使用

```go
go func(val int) {
    result := doBusinessLogic(val)
    resultString := fmt.Sprintf("%d -> %d", val, result)
    chOut <- resultString
}(val)
```

- この部分では、`doBusinessLogic` 関数を呼び出す新しいゴルーチンを作成しています。入力された `val` を使って計算を行い、その結果を `chOut` チャネルに送信します。これにより、他の処理（次の値の受信など）をブロックすることなく、並行して実行されます。

### チャネル（channel）

- **定義**: チャネルはゴルーチン間でデータを送受信するためのパイプです。チャネルを使うことで、ゴルーチン同士が安全にデータをやりとりできます。

- **特徴**:
  - **型安全**: チャネルは特定のデータ型を持ち、その型のデータのみを送信・受信できます。
  - **ブロッキング**: チャネルの送信（`ch <- value`）は受信側が準備できるまでブロックされます。逆に、受信（`value := <-ch`）も送信者がデータを送るまでブロックされます。これにより、データの整合性が保たれます。

#### コード内のチャネルの使用

```go
chIn := make(chan int)  // intのチャネルを生成。送信用
chOut := make(chan string) // stringのチャネルを生成。受信用
```

- `chIn` と `chOut` の二つのチャネルを作成しています。`chIn` は整数を受け取るために使用され、`chOut` は結果を文字列として返すために使用されます。

```go
chIn <- v   // その値をchInに送信
```

- ここでは、`vals` の各要素を `chIn` に送信しています。この送信は、`runThingsConcurrently` 関数内のループで受信されます。

```go
for v := range chOut {
    fmt.Println(v)
}
```

- `chOut` から受信したデータを出力します。`chOut` が閉じられるまでこのループは続きます。これは、全てのゴルーチンが処理を完了し、結果を送信した後に `chOut` が閉じられることで、ループが終了します。

### 並行処理の流れ

1. **データの送信**: `main` 関数で `vals` の各値を `chIn` に送信します。
2. **ゴルーチンの開始**: `runThingsConcurrently` 内で、`chIn` から受信した値ごとに新しいゴルーチンが開始されます。
3. **ビジネスロジックの実行**: 各ゴルーチン内で `doBusinessLogic` が呼び出され、計算が行われます。
4. **結果の送信**: 計算結果が `chOut` に送信されます。
5. **結果の受信**: `main` 関数で `chOut` から結果を受信し、出力します。