前の例では、バッファーなしのチャネルを作成しました。バッファリングされていないチャネルを使用してデータをロードする場合、別のゴルーチンで別のチャネルがフェッチされるまでローダーはブロックされます。同様に、チャネルにデータが入力されていない場合、受信機はチャネルからデータを取得しようとするときにもブロックされます。送信操作と受信操作は同期して行われます。
goroutine とチャネルを組み合わせて、同時印刷の例を通してその使用法を示してみましょう。
package main
import (
"fmt"
)
func printer(c chan int) {
//データが届くまで無限ループ開始
for {
// チャネルからデータを取得する
data := <-c
//0が送信されたら処理終了
if data == 0 {
break
}
// データを表示する
fmt.Println(data)
}
// mainに処理が終わったことを通知(OK!)
c <- 0
}
func main() {
// チャネル作成
c := make(chan int)
//printerをgoで実行し、チャネルを渡す
go printer(c)
for i := 1; i <= 10; i++ {
// データをprinterに渡す
c <- i
}
//printerにデータが無くなったことを通知
c <- 0
//printerの処理が終わるまで待つ(OKだよ!)
<-c
}
コードを実行すると、出力は次のようになります。
1
2
3
4
5
6
7
8
9
10
コードの説明は次のとおりです。
- 10行目は無限ループを作成し、16行目で取得したデータが0の場合のみループを抜けます。
- 13 行目では、関数パラメータによって渡されたチャネルから整数値が取得されます。
- 行 21、整数値を出力します。
- 25 行目で、ループを終了するときに、チャネルを通じて main() 関数に作業が完了したことが通知されます。
- 32 行目は、ゴルーチン間通信用の整数チャネルを作成します。
- 35行目ではprinter()関数を並行して実行するゴルーチンを作成しています。
- 37 行目、数値サイクルを構築し、チャネルを通じてプリンターによって構築されたゴルーチンに 1 から 10 までの数値を送信します。
- 44 行目では、チャネルに 0 が渡され、前のデータ処理が完了した後にループが終了することを示します。
- 47 行目では、データが送信された後、同時実行性とスケジュールの理由により、タスクが同時に実行されます。ここでは、main() を終了する前に、プリンターの 25 行目がデータを返すのを待つ必要があります。
この例の設計パターンは、典型的な生産者と消費者です。プロデューサは 37 行目のループで、コンシューマは printer() 関数です。例全体では 2 つのゴルーチンを使用しています。1 つは main() で、もう 1 つは 35 行目の printer() 関数によって作成されたゴルーチンです。 2 つのゴルーチンは、32 行目で作成されたチャネルを通じて通信します。このチャネルには以下の 2 つの機能があります。
- データ転送: 40 行目でデータを送信し、13 行目でデータを受信します。
- 制御命令:セマフォに似た機能。ゴルーチンの動作を同期します。関数は次のように簡単に説明されます。
- 44行目:「データがありません!」
- 25行目:「分かった!」
- 47行目:「電話してください!」
「 Go 言語の同時印刷 (チャネルによって実装)」についてわかりやすく解説!絶対に観るべきベスト2動画
Go言語とは?|プログラミング言語のGo言語について3分でわかりやすく解説します【プログラミング初心者向け】
【初心者必見!】Go言語とは?できることや学ぶメリット・将来性について解説