Channel と通信
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 60/107。
Channels は、ゴルーチン間の通信のための Go の主要な仕組みです。メモリを直接共有するのではなく、ゴルーチンは channels を通じて値を送受信します。これは Go の哲学である「メモリを共有して通信するのではなく、通信によってメモリを共有する」に従ったものです。
chanキーワードを使用して、make関数でチャネルを作成します。
messages := make(chan string)値の送受信には矢印演算子 <- を使用します。矢印は data の流れる方向を示します。
func main() {
messages := make(chan string)
go func() {
messages <- "hello" // チャネルに送信
}()
msg := <-messages // チャネルから受信
fmt.Println(msg) // 出力: hello
}デフォルトでは、もう一方が準備できるまで送受信はブロックします。goroutine が "hello" を送信すると、main が受信できる状態になるまで待機します。このブロッキング動作によって組み込みの同期が実現されるため、time.Sleep は必要ありません。
これ以上値が送信されないことを示すために、channelをcloseできます。受信側はこれを検出できます。
close(messages)
msg, ok := <-messages
if !ok {
fmt.Println("Channel closed")
}チャネルには型があります。chan string は文字列だけを運べます。この型安全性により、goroutine は正しい data 型で通信でき、実行時ではなくコンパイル時にエラーを検出できます。
チャレンジ
簡単goroutine が channels を通じてどのように通信するかを示す message relay システムを構築しましょう。message が異なる処理ステージ間を流れ、各ステージが同時に実行される pipeline を作成します。
コードを 2 つのファイルに分けて整理します。
relay.go: message 処理 pipeline を Define します。Messagestruct を作成し、ID(int) とContent(string) の fields を定義します。pipeline の異なるステージを表す 3 つの関数を実装します。
Producer(messages []Message, out chan Message)- message のスライスを受け取り、それぞれを output channel に送信します。すべての message を送信した後、完了を通知するために channel を閉じます。Transformer(in chan Message, out chan Message)- input channel から message を受け取り、各 message の content を大文字に変換して、変換後の message を output channel に送信します。input channel が閉じられたとき(2 つの値を受け取る形式を使用して検出)、output channel を閉じます。Consumer(in chan Message) []string- input channel からすべての message を受け取り、formatted string のスライスに collect します。各 string は次の形式に従う必要があります:Message [ID]: [Content]。channel が閉じられたらスライスを返します。
main.go: pipeline を設定し、concurrent な message flow を調整します。message の number を読み取り、続いて各 message の ID と content を読み取ります。message を作成し、3 つの pipeline ステージを connect する 2 つの channel を設定します。Producer と Transformer を goroutine として Launch し、その後 main goroutine で Consumer を実行して results を collect します。各 result を別々の line に Print します。
次の input が提供されます。
- Line 1: message の number (integer)
- Following lines: 各 message について 2 行 - message ID (integer)、続いてその content (string)
たとえば、次の input が与えられた場合:
2
1
hello world
2
go channelsoutput は次のようになります。
Message 1: HELLO WORLD
Message 2: GO CHANNELSpipeline は次のように流れます: Producer が message を送信 → Transformer が uppercase に converts → Consumer が results を collect して formatted します。各矢印は concurrent なステージを connect する channel を表します。uppercase への変換には strings package を使用します。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// メッセージの数を読み取る
nLine, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(nLine))
// メッセージを読み取る
messages := make([]Message, n)
for i := 0; i < n; i++ {
idLine, _ := reader.ReadString('\n')
id, _ := strconv.Atoi(strings.TrimSpace(idLine))
contentLine, _ := reader.ReadString('\n')
content := strings.TrimSpace(contentLine)
messages[i] = Message{ID: id, Content: content}
}
// TODO: パイプラインのステージを接続する2つのチャネルを作成する
// TODO: Producerをgoroutineとして起動する
// TODO: Transformerをgoroutineとして起動する
// TODO: メインのgoroutineでConsumerを実行し、結果を収集する
// TODO: 各結果を別々の行に出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ