Go練習問題4-04:チャネルの選択を理解しよう

記事内に商品プロモーションを含む場合があります

Go言語の初心者向け問題4-04:チャネルの選択を理解しよう

この問題を解くために必要な知識:
レベル1~3の知識メモリ管理の基本エラーハンドリングとカスタムエラーチャネルの選択タイムアウト処理パッケージinit関数ファイル操作エンコーディングとデコーディングテストの書き方ベンチマークテストドキュメントの生成

<<前の問題 問題集Top 次の問題>>

Go言語の文法「チャネルの選択」とは





ここではチャネルの選択の意味や使い方を復習します。必要ない方はここをクリックして練習問題へ飛びましょう。

チャネルの選択とは、select文を用いて、複数のチャネルのいずれかからメッセージが到着するのを待つことを指します。

これにより、複数のゴルーチンが並行して実行されている場合でも、受信できるチャネルがどれであるかに応じて処理を切り替えることが可能です。

select文の基本的な使い方

以下に、select文を使用した基本的な例を示します。

select {
case msg := <-ch1:
    fmt.Println(msg)
case msg := <-ch2:
    fmt.Println(msg)
default:
    fmt.Println("No message received")
}

このコードでは、ch1ch2という2つのチャネルからのメッセージを待ち受け、最初にメッセージが到着した方のケースを実行します。

defaultケースは、どのチャネルからもメッセージが到着しなかった場合に実行されます。

なぜ select が便利なのか?

select文は、以下のような場面で特に有効です。

  1. 複数のチャネルからのメッセージを非同期に処理する場合
    select文を使用することで、どのチャネルからメッセージが受信されるかを動的に判断し、それに応じて処理を行うことができます。
  2. タイムアウトの処理
    以下のコード例のように、select文を使って、指定した時間内にメッセージが到着しなかった場合にタイムアウト処理を行うことも可能です。
select {
case msg := <-ch1:
    fmt.Println("Received:", msg)
case <-time.After(5 * time.Second):
    fmt.Println("Timeout")
}

このコードでは、チャネルch1から5秒以内にメッセージが受信されなかった場合にタイムアウト処理が実行されます。

まとめ

select文は、Go言語における並行処理の強力なツールです。複数のチャネルを扱う際に、この文を活用することで、効率的で柔軟な並行プログラムを作成することが可能になります。

Go練習問題4-4:複数のチャネルからのメッセージを処理するプログラムを作成しよう

複数のチャネルからメッセージを受信し、そのメッセージを処理するプログラムを作成しましょう。

select文を使って、どちらかのチャネルからメッセージが受信された場合に、そのメッセージを表示することが目標です。

この問題の要件

以下の要件に従ってコードを完成させてください。

  • メッセージを送信する関数 sendMessage を定義し、指定されたチャネルにメッセージを送信すること。
  • 2つのチャネル ch1ch2 を作成すること。
  • ゴルーチンを使って、ch1 に「こんにちは、チャネル1からのメッセージです!」というメッセージを送信し、ch2 に「こんにちは、チャネル2からのメッセージです!」というメッセージを送信すること。
  • select文を使って、どちらかのチャネルからメッセージが受信された場合に、そのメッセージを表示すること。
  • メッセージが表示された後、「プログラムが終了しました。」と表示すること。

ただし、以下のような実行結果となるコードを書くこと。

*****↓↓正解コードの実行結果の例↓↓*****

こんにちは、チャネル1からのメッセージです!
プログラムが終了しました。

(出力結果はタイミングにより異なる場合があります。)

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。

1.package main パッケージの宣言
2.import 文によるパッケージのインポート
 2-1. fmt パッケージのインポート
3.sendMessage 関数の定義
 3-1. 引数としてチャネル ch とメッセージ message を受け取り、メッセージをチャネルに送信
4.main 関数の定義
 4-1. 2つのチャネル ch1ch2 の作成
 4-2. ゴルーチンで sendMessage 関数を呼び出し、メッセージを各チャネルに送信
 4-3. select 文によるチャネルの選択
  4-3-1. ch1 からのメッセージを受信し、fmt.Println で表示
  4-3-2. ch2 からのメッセージを受信し、fmt.Println で表示
 4-4. プログラム終了のメッセージを fmt.Println で表示

ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

package main

import (
    "fmt"
)

// メッセージを送信する関数
// 指定されたチャネルにメッセージを送信します
func sendMessage(ch chan string, message string) {
    /* 【穴埋め問題1】
    ここにメッセージをチャネルに送信するコードを書いてください。
    */
}

// メイン関数
// 複数のチャネルからのメッセージを受信して処理します
func main() {
    // 2つのチャネルを作成
    ch1 := make(chan string)
    ch2 := make(chan string)

    // ゴルーチンでメッセージを送信
    go sendMessage(ch1, "こんにちは、チャネル1からのメッセージです!")
    go sendMessage(ch2, "こんにちは、チャネル2からのメッセージです!")

    // チャネルの選択を行うselect文
    // どちらかのチャネルからメッセージが受信されると、それに応じた処理が行われます
    select {
    /* 【穴埋め問題2】
    ここにチャネル1からのメッセージを受信し、表示するコードを書いてください。
    */
    /* 【穴埋め問題3】
    ここにチャネル2からのメッセージを受信し、表示するコードを書いてください。
    */
    }

    fmt.Println("プログラムが終了しました。") // プログラムの終了メッセージ
}

 

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

解答例と解説

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

package main

import (
    "fmt"
)

// メッセージを送信する関数
// 指定されたチャネルにメッセージを送信します
func sendMessage(ch chan string, message string) {
    ch <- message // メッセージをチャネルに送信
}

// メイン関数
// 複数のチャネルからのメッセージを受信して処理します
func main() {
    // 2つのチャネルを作成
    ch1 := make(chan string)
    ch2 := make(chan string)

    // ゴルーチンでメッセージを送信
    go sendMessage(ch1, "こんにちは、チャネル1からのメッセージです!")
    go sendMessage(ch2, "こんにちは、チャネル2からのメッセージです!")

    // チャネルの選択を行うselect文
    // どちらかのチャネルからメッセージが受信されると、それに応じた処理が行われます
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1) // チャネル1からのメッセージを受信
    case msg2 := <-ch2:
        fmt.Println(msg2) // チャネル2からのメッセージを受信
    }

    fmt.Println("プログラムが終了しました。") // プログラムの終了メッセージ
}

正解コードの解説

この解説では、各コードブロックごとにどのような処理が行われているかを説明し、特に「チャネルの選択」について詳しく解説します。

パッケージの宣言

package main

package main は、Goプログラムのエントリーポイントであることを示す特別なパッケージ宣言です。このプログラムが実行可能なファイルであることを意味します。

パッケージのインポート

import (
    "fmt"
)

import 文を使って、外部のパッケージをプログラムに取り込みます。ここでは、標準ライブラリの fmt パッケージをインポートしています。このパッケージは、コンソールにテキストを出力するために使用されます。

sendMessage 関数の定義

// メッセージを送信する関数
// 指定されたチャネルにメッセージを送信します
func sendMessage(ch chan string, message string) {
    ch <- message // メッセージをチャネルに送信
}

sendMessage 関数は、指定されたチャネル (ch) にメッセージ (message) を送信するための関数です。ch <- message の部分は、messagech に送る操作を行います。チャネルは、Goの並行処理でゴルーチン間の通信を行うために使用されます。

main 関数の定義

// メイン関数
// 複数のチャネルからのメッセージを受信して処理します
func main() {
    // 2つのチャネルを作成
    ch1 := make(chan string)
    ch2 := make(chan string)

main 関数は、Goプログラムのエントリーポイントであり、最初に実行される関数です。ここでは、2つのチャネル (ch1ch2) を作成しています。

これらのチャネルを通じて、ゴルーチン間でメッセージを送受信します。

ゴルーチンの開始

    // ゴルーチンでメッセージを送信
    go sendMessage(ch1, "こんにちは、チャネル1からのメッセージです!")
    go sendMessage(ch2, "こんにちは、チャネル2からのメッセージです!")

go キーワードを使って、非同期に実行される2つのゴルーチンを開始します。

それぞれのゴルーチンは、sendMessage 関数を呼び出し、指定されたチャネルにメッセージを送信します。これにより、ch1ch2 にメッセージが送られます。

チャネルの選択

    // チャネルの選択を行うselect文
    // どちらかのチャネルからメッセージが受信されると、それに応じた処理が行われます
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1) // チャネル1からのメッセージを受信
    case msg2 := <-ch2:
        fmt.Println(msg2) // チャネル2からのメッセージを受信
    }

ここがこのコードの中で最も重要な部分です。select 文を使って、複数のチャネルからのメッセージを待ち受けます。

どちらかのチャネル (ch1 または ch2) からメッセージが受信されると、そのメッセージを fmt.Println でコンソールに表示します。

select 文は、どのチャネルから先にメッセージが届くか分からない場合でも、安全に処理を行うことができます。

プログラムの終了メッセージ

    fmt.Println("プログラムが終了しました。") // プログラムの終了メッセージ

全ての処理が終わった後で、プログラムの終了メッセージをコンソールに表示します。

まとめ

このコードは、Goの並行処理の基本を学ぶのに最適な例です。特に、「チャネルの選択」を行う select 文は、複数のゴルーチン間での通信を効率的に管理するために非常に重要な機能です。

初心者の方は、まずはこのコードを実際に動かしてみて、どのようにメッセージが処理されるかを確認してください。

<<前の問題 問題集Top 次の問題>>

この問題への質問・コメント

この問題を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    Go練習問題集へ戻る
    トップページへ戻る