Kotlin Academy · レッスン

Channelの基本:send、receive、close

Channelを作成し、値を送受信して、適切に閉じます。

レッスン 1/413 ステップ

「Channelの基本:send、receive、close」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

Channelとは

Channelはコルーチン間の通信に使うプリミティブです。ブロッキングキューに似ていますが、待機時には一時停止します。片側が送信し、もう片側が受信するパイプだと考えてください。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val channel = Channel<Int>()
    launch {
        for (i in 1..5) channel.send(i)
        channel.close()
    }
    for (x in channel) println(x) // 1 2 3 4 5
}

sendとreceive

send(value)は受信側の準備が整うまで一時停止します。receive()は値が利用可能になるまで一時停止します。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val ch = Channel<String>()
    launch {
        ch.send("ping")
        println("sent")
    }
    val msg = ch.receive()
    println("received: $msg")
}

Channelを閉じる

channel.close()は、これ以上値が送信されないことを通知します。受信側は残っているバッファ内の値を受け取った後、forループを終了します。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val ch = Channel<Int>(3)
    launch {
        repeat(3) { ch.send(it) }
        ch.close()  // signal done
    }
    for (v in ch) println(v) // 0 1 2, then loop ends
    println("Channel consumed")
}

isClosedForSend / isClosedForReceive

isClosedForSend(これ以上送信できない)とisClosedForReceive(すべての値を消費済み)を使ってチャネルの状態を確認します。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val ch = Channel<Int>()
    launch {
        ch.send(1)
        ch.close()
    }
    println(ch.receive())
    println("isClosedForReceive: ${ch.isClosedForReceive}")
}

produceビルダー

produce { }はプロデューサーコルーチンを作成してReceiveChannelを返すコルーチンビルダーです。ブロックが終了するとチャネルは自動的に閉じられます。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.squares(n: Int) = produce<Int> {
    for (i in 1..n) send(i * i)
}
fun main() = runBlocking {
    val sq = squares(5)
    for (v in sq) println(v) // 1 4 9 16 25
}

consumeEach拡張

consumeEach { }はチャネルを反復処理し、ブロックが例外をスローするとチャネルをキャンセルして、リソースリークを防ぎます。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val ch = produce<String> {
        send("a"); send("b"); send("c")
    }
    ch.consumeEach { println(it) }
}

tryReceiveとtrySend

一時停止しない代替手段として、trySend/tryReceiveは一時停止せず、ChannelResultをすぐに返します。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val ch = Channel<Int>(1)
    val sendResult = ch.trySend(42)
    println("Sent: ${sendResult.isSuccess}")
    val recvResult = ch.tryReceive()
    println("Got: ${recvResult.getOrNull()}")
}

ファンアウト:複数のコンシューマー

複数のコルーチンが同じチャネルから受信して作業を分担できます。これがファンアウトパターンです。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val tasks = produce { repeat(6) { send(it) } }
    repeat(3) { worker ->
        launch {
            for (task in tasks) {
                println("Worker $worker: task $task")
            }
        }
    }
}

ファンイン:複数のプロデューサー

複数のコルーチンが同じチャネルに送信し、それぞれの出力を結合できます。これがファンインパターンです。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val results = Channel<String>()
    repeat(3) { i ->
        launch { results.send("result-$i") }
    }
    launch {
        repeat(3) { println(results.receive()) }
        results.close()
    }
}

パイプラインパターン

チャネルをパイプラインとして連結し、各ステージが1つのチャネルから読み取り、別のチャネルに書き込むようにします。データ処理ワークフローに適しています。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.numbers() = produce { for (i in 1..5) send(i) }
fun CoroutineScope.doubled(input: ReceiveChannel<Int>) = produce { for (v in input) send(v * 2) }
fun main() = runBlocking {
    val nums = numbers()
    val doubled = doubled(nums)
    for (v in doubled) println(v) // 2 4 6 8 10
    coroutineContext.cancelChildren()
}

ChannelとFlowの違い

Channelはホットで状態を持ち、消費処理とは独立して存在します。Flowはコールドで宣言的であり、収集するたびに再開します。データストリームにはFlowを優先し、プロデューサーとコンシューマー間の通信にはChannelを使用します。

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.flow.*
// Flow (cold): each collect() re-runs the block
// Channel (hot): shared; multiple receivers share the same stream
fun main() = runBlocking { println("Flow = cold; Channel = hot") }

クイックチェック

channel.close()を呼び出すとどうなりますか。

復習

sendとreceiveは、相手側の準備が整うまで一時停止します。close()は完了を通知します。自動的に閉じるプロデューサーにはproduce、安全な反復処理にはconsumeEachを使用し、ファンアウト/ファンインパターンにはチャネルを使用します。

無料で開始

AI チューターと学ぶ Kotlin — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
51
レッスン
203

よくある質問

「Channelの基本:send、receive、close」レッスンは無料ですか?

はい。「Channelの基本:send、receive、close」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「Channelの基本:send、receive、close」で何を学びますか?

Channelを作成し、値を送受信して、適切に閉じます。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「Channelの基本:send、receive、close」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Channelの基本:send、receive、close
  2. Channelの種類:Rendezvous、Buffered、Conflated、Unlimited
  3. 共有状態のためのMutexとSemaphore
  4. Actorと構造化された状態管理
← Kotlin Academyに戻る