Menu
Coddy logo textTech

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 型で通信でき、実行時ではなくコンパイル時にエラーを検出できます。

challenge icon

チャレンジ

簡単

goroutine が channels を通じてどのように通信するかを示す message relay システムを構築しましょう。message が異なる処理ステージ間を流れ、各ステージが同時に実行される pipeline を作成します。

コードを 2 つのファイルに分けて整理します。

  • relay.go: message 処理 pipeline を Define します。

    Message struct を作成し、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 channels

output は次のようになります。

Message 1: HELLO WORLD
Message 2: GO CHANNELS

pipeline は次のように流れます: 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: 各結果を別々の行に出力する
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ