Go言語(プログラミング)入門メモ⑱

## チャネルを使用してゴルーチンと値のやり取りを行う
並行で実行したゴルーチンと値の受け渡しをしたい場合、関数やメソッドのようにreturnで値を返すという方法ではやり取りが行えない。ゴルーチン間ではChannel（チャネル）を使ってデータの受け渡しを行う。

## チャネルの使用
まず、main関数でint型のスライスを作る。次にmake関数でチャネルを作る。「c := make(chan int)」とすると、int型のチャネル(chan)が変数cに代入される。チャネルを作る際は、make関数の引数を「chan データ型」のように書いて、やりとりするデータ型を指定する。
作ったチャネルをゴルーチンに引数として渡す。そして、チャネルでゴルーチンから値を受け取って変数xに格納するために、<-演算子を使って「x := <-c」と書く。
~~~
func main() {
    s := []int{1,2,3,4,5}
    c := make(chan int)  //チャネルの宣言
    go gorutine1(s, c) 　//ゴルーチンにチャネルを渡す
    x := <-c　　　　　　  //チャネルから値を受け取って変数xに代入する
    fmt.Println(x)
}
~~~
次にゴルーチンを作成する。(関数goroutine1の作成)関数内で変数sumを0で初期化し、for文とrangeでスライスsを読み込む。vの値をループで変数sumに足し合わせていく。最後に<-演算子を使って「c <- sum」と書くことで、変数sumの値をチャネルに送信する。コードを実行するとスライスの値を足した値が表示される。
~~~
func goroutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s{
        sum += v
    }
    c <- sum
}

//以下のように表示される
15
~~~
追加で関数を作成して、main関数で受信するデータをもう一つ増やす。
イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/95b061f0-1305-b73a-5409-3d8cd01232df.png)


中の処理はgoroutine1と同じで関数名だけgoroutine2にした関数を作成する。
goroutine1関数とgoroutine2関数はそれぞれ「c <- sum」で、同じチャネルにデータを渡す。
~~~
func goroutine2(s []int, c chan int) {
    sum := 0
    for _, v := range s{
        sum += v
    }
    c <- sum
}
~~~
main関数では、goroutine2関数の引数にスライスとチャネルを指定して、goをつけて実行する。さらに、データをもう一つ受信する変数yを作り、「y := <-c」でチャネルからデータを受け取る。実行すると2つの関数から受け取ったデータが表示された。
~~~
func main() {
 s := []int(1,2,3,4,5)
 c := make(chan int)
 go goroutine1(s, c)
 go goroutine2(s, c)
 x := <-c
 fmt.Println(x)
 y := <-c
 fmt.Println(y)
}

以下のように出力される
15
15
~~~
goroutin1関数からは「x := <-c」、goroutine2関数からは「y := <-c」でデータを順番に受信している。チャネルに対して先に送信されたデータを先に受信するため、チャネルはFIFO（先入先出）のデータ構造というイメージ。

## バッファありチャネルを使った値のやり取り
これまではバッファなしチャネルを用いていた。バッファありチャネルは「ch := make(chan int, 2)」と書いて変数chにmakeでチャネルを作成する。
作成時に型をint型に指定しているのに加えて、バッファを2に設定している。
このチャネルにch <- 100と書いて100を渡してチャネルの要素数を確認すると、1が表示される。
~~~
func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
}

//以下のように表示される
1
~~~
次にch <- 200と書いて200を渡してから再び要素数を確認すると、2が表示される。
~~~
func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))
}

//以下のように表示される
1
2
~~~
さらにch <- 300と書いて300を渡してから再び要素数を確認しようとすると、エラーとなる。このチャネルはバッファが2なので3つ目の要素を入れようとするとエラーとなる。
~~~
func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))
    ch <- 300
    fmt.Println(len(ch))
}

//以下のように表示される
1
2
fatall error...(以下略)
~~~
エラーを回避するためにch <- 300でチャネルに値を送信する前に「x := <-ch」で受信して値を取り出す。取り出した後のチャネルの要素数は1なので300を渡してもエラーにはならない。
~~~
func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))
    
    x := <-ch
    fmt.Println(x)
    fmt.Println(len(ch))
    
    ch <- 300
    fmt.Println(len(ch))
}

//以下のように表示される
1
2
100     ←先に入れた100が取り出されている
1
2
~~~

## rangeとcloseでチャネルから値を取り出す。
バッファなしチャネルのコードを再掲する。
~~~
func goroutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s{
        sum += v
    }
    c <- sum
}

func main() {
    s := []int{1,2,3,4,5}
    c := make(chan int)  
    go gorutine1(s, c) 　
    x := <-c　　　　　　  
    fmt.Println(x)
}
~~~
このコードでは、goroutine1関数でスライスの中身をすべて合計してからチャネルに渡しているが、これを合計していく途中家庭もチャネルに送信して表示してみる。
イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/cbd2565e-1bbc-690c-c753-b73753614a96.png)
まず、goroutine1関数におけるチャネルへの送信「c <- sum」をfor文の中に書くことでループごとにチャネルに値を送信する。
~~~
func goroutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s{
        sum += v
        c <- sum   // for文の中に書く
    }
}
~~~

main関数内では、「for i := range c {fmt.Println(i)}」と書く。
~~~
func main() {
    s := []int{1,2,3,4,5}
    c := make(chan int)  
    for i := range c {       //for文でチャネルから1つずつ値を取り出す
        fmt.Println(i)
    }
}
~~~
ただし、rangeはチャネルから値が送信されるのをずっと待ってしまうため。チャネルにはこれ以上値を送信しない、ということを伝えるためにclose()をする必要がある。
~~~
func goroutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s{
        sum += v
        c <- sum   // for文の中に書く
    }
    close(c)       //チャネルを閉じる
}

//以下のように表示される
1
3
6
10
15
~~~

## 学習に使用した教材
・『入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』M.A EduTech
https://www.udemy.com/course/golang-webgosql/?utm_medium=udemyads&utm_source=bene-msa&utm_campaign=responsive&utm_content=top-1&utm_term=general&msclkid=81e2f24a32cc185d275d953d60760226&couponCode=NEWYEARCAREERJP

・『シリコンバレー一流プログラマーが教える Goプロフェッショナル大全』酒井 潤 (著)
https://www.amazon.co.jp/%E3%82%B7%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%90%E3%83%AC%E3%83%BC%E4%B8%80%E6%B5%81%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%8C%E6%95%99%E3%81%88%E3%82%8B-Go%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E5%A4%A7%E5%85%A8-%E9%85%92%E4%BA%95-%E6%BD%A4/dp/4046070897