Go言語入門 : goroutineとチャネルを使った簡単並行処理

Go言語の特徴の一つに、軽量な並行処理を簡単に実現できる「goroutine」と「チャネル」があります。本記事では、goroutineとチャネルの基本概念とその使い方について、学んだことをアウトプットしたいと思います。

# goroutineとは

goroutineは、Go言語で使われる軽量なスレッドのようなもので、並行処理を簡単に実現するための機能です。goroutineを使うと、複数の処理を同時に実行することができます。

# goroutineの基本概念
軽量な並行処理: goroutineは少ないメモリで多くの仕事を同時にこなせます。
簡単な使い方: 関数呼び出しの前に`go` キーワードを使うだけで、簡単にgoroutineをスタートできます。

```
go 関数名()
```

# 実例1: 基本的なgoroutine
以下は、goroutineを使ってメッセージを表示する簡単な例です。
※ [原神](https://genshin.hoyoverse.com/ja/home)でお馴染みの[ナヴィアさん](https://genshin.hoyoverse.com/ja/character/Fontaine?char=7)のセリフを例にしています。

```
package main

import (
    "fmt"
    "time"
)

func say(message string) {
    for i := 0; i < 2; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(message)
    }
}

func main() {
    go say("ローズクラッカー！ 「棘薔薇の会」特注版よ。") // ゴルーチンで「ローズクラッカー！ 「棘薔薇の会」特注版よ。」と表示
    say("ファイアー！ ま、岩元素なんだけどね。")    // メインの関数で「ファイアー！ま、岩元素なんだけどね。」と表示
}
```
### 実行結果(The Go Playground)
このプログラムを実行すると、以下のような結果が表示されます。

![スクリーンショット 2024-06-29 21.15.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3358559/1de013c3-9608-c8a8-fd22-59165572c2f5.png)

# チャネルとは
チャネルは、goroutine同士がメッセージをやり取りするためのものです。これを使うと、安全に情報を交換できます。

# 実例2: チャネルでメッセージを送受信
次に、VTuberの星街すいせいさんが星詠みにメッセージを送り、星詠みがそのメッセージを受け取る例を見てみましょう。

```
package main

import (
    "fmt"
    "time"
)

// メッセージを送る関数
func sendMessage(messages chan string) {
    messages <- "彗星のごとく現れたスターの原石！アイドルVTuberの星街すいせいです！！" // メッセージを送る
    time.Sleep(time.Second)
    messages <- "すいちゃんは～？" // もう一つメッセージを送る
    time.Sleep(time.Second)
    messages <- "今日もかわいいー！" // さらにもう一つメッセージを送る
}

// 星詠みがメッセージを受け取る関数
func receiveMessage(messages chan string) {
    msg := <-messages // メッセージを受け取る
    fmt.Println("星詠み:", msg)
    msg = <-messages // 次のメッセージを受け取る
    fmt.Println("星詠み:", msg)
    msg = <-messages // 次のメッセージを受け取る
    fmt.Println("星詠み:", msg)
}

func main() {
    messages := make(chan string) // メッセージのやり取りをするチャネルを作成

    go sendMessage(messages)  // 星街すいせいgoroutineをスタート
    go receiveMessage(messages) // 星詠みgoroutineをスタート

    // goroutineが終わるのを少し待つ
    time.Sleep(3 * time.Second)
}
```

### 実行結果(The Go Playground)
このプログラムを実行すると、以下のような結果が表示されます。

![スクリーンショット 2024-06-29 21.14.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3358559/ffbf12a7-0fbb-b519-197f-203d28b14d39.png)

# バッファ付きチャネル
バッファ付きチャネルは、メッセージを一定数ためておくことができるチャネルです。これにより、送信側が受信側の準備ができるまで待つ必要がなくなります。
makeを使ってチャネルを作成して、バッファのサイズを第2引数に渡すことで定義できます。

```
ch := make(chan int, 3)
```

# 実例3: バッファ付きチャネルでメッセージを送受信
次に、バッファ付きチャネルを使った例を見てみましょう。

```
package main

import (
    "fmt"
    "time"
)

// メッセージを送る関数
func sendMessage(messages chan string) {
    messages <- "彗星のごとく現れたスターの原石！アイドルVTuberの星街すいせいです！！" // メッセージを送る
    time.Sleep(time.Second)
    messages <- "すいちゃんは～？" // もう一つメッセージを送る
    time.Sleep(time.Second)
    messages <- "今日もかわいいー！" // さらにもう一つメッセージを送る
}

// 星詠みがメッセージを受け取る関数
func receiveMessage(messages chan string) {
    for i := 0; i < 3; i++ {
        msg := <-messages // メッセージを受け取る
        fmt.Println("星詠み:", msg)
    }
}

func main() {
    messages := make(chan string, 3) // バッファ付きチャネルを作成

    go sendMessage(messages)  // 星街すいせいgoroutineをスタート
    go receiveMessage(messages) // 星詠みgoroutineをスタート

    // goroutineが終わるのを少し待つ
    time.Sleep(3 * time.Second)
}
```
### プログラムの説明
バッファ付きチャネルの作成: messages := make(chan string, 3) というように、バッファサイズを指定してチャネルを作成します。
メッセージの送信: sendMessage 関数は、星街すいせいさんがメッセージをチャネルに送る役割をします。
メッセージの受信: receiveMessage 関数は、星詠みがチャネルからメッセージを受け取る役割をします。バッファ付きチャネルを使うことで、受信が遅れても送信側が待たされることなくメッセージを送ることができます。

実行結果
このプログラムを実行すると、以下のような結果が表示されます。

![スクリーンショット 2024-06-29 21.14.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3358559/375b67a6-6df4-d31f-4c28-3e9e3bf9d712.png)


# まとめ
go キーワードでgoroutineを簡単にスタートできる。
チャネルを使って、メッセージを安全にやり取りを行う。
バッファ付きチャネルを使うことで、送信側が受信側を待たずにメッセージを送信できる。