第61回Kotlin dojoを開催した

今週は第61回を開催しました!

引き続きKotlin Hands-onをすすめています。

 

前回の様子はコチラ↓ 

kdnakt.hatenablog.com

 

 

[第61回の様子]

2021/4/21に第61回を開催した。今週もいつもどおり水曜日開催。

 

参加メンバーは自分をいれて3名。本当はもう1人参加予定だったのだけれど急用でやむなく不参加に。

今回も自分がドライバ役で参加した。

 

勉強会本編の内容としては、ひき続きIntroduction to Coroutines and Channelsハンズオンを進めた。第7章 Showing progressの最後の部分を読み、第8章Channelsのタスク直前までを読み込んだ。

 

[学んだことや疑問点]

  • Introduction to Coroutines and Channels: 7. Showing progress
    • タスクの答え合わせが終わり、最後の文章と図だけ残っていた
    • この章の実装では、updateResults()が呼ばれるのが直列に実行されるリクエストの終わりだけで、並列処理になっていなかった
    • これを並列処理にして、中間結果を利用してupdateResultsを呼ぶにはどうすればよいだろうか?
    • 次の章で学ぶ、Channnelが解決してくれる
  • Introduction to Coroutines and Channels: 8. Channels
    • 共有の可変の状態を使ってコーディングするのは難しく、間違いが起きやすい
    • コミュニケーションによって情報を共有すればよい:coroutineの場合channnelを使う
    • 複数のproducerがチャンネルに送信できるし、複数のconsumerがチャンネルから受信できる
    • 注意:1つのメッセージはいずれかのconsumerに消費されるとチャンネルから削除される
    • チャンネルはqueueのようなもの:違いは、send操作とreceive操作がsuspendされる点
    • send時にチャンネル内のメッセージ上限に達している場合は待ちが発生する、receive時にはメッセージが空だと待ちが発生する
    • チャンネルのインターフェースは次の通り
interface SendChannel<in E> {
    suspend fun send(element: E)
    fun close(): Boolean
}

interface ReceiveChannel<out E> {
    suspend fun receive(): E
}    

interface Channel<E> : SendChannel<E>, ReceiveChannel<E>
    • producerがclose()を呼び出すともうメッセージが来ない
    • ライブラリで提供されているチャンネルにはいくつか種類がある
    • 無制限チャンネル(Unlimited channel):queueと一番近い。メモリの許す限りメッセージを追加できる(ダメな場合OutOfMemoryException)。
    • バッファチャンネル:メッセージの件数に上限がある。上限に達している場合、次のsend操作は中断される
    • ランデブーチャンネル:保持できるメッセージの件数が0、すなわりsendとreceive操作がお互いを待ち続ける
    • 合成チャンネル(conflated channel):新しいメッセージがくると古いメッセージが上書きされる。send操作はsuspendされない。
    • チャンネルのインスタンス作成は以下のように行う
val rendezvousChannel = Channel<String>()
val bufferedChannel = Channel<String>(10)
val conflatedChannel = Channel<String>(CONFLATED) // Channel.CONFLATED
val unlimitedChannel = Channel<String>(UNLIMITED) // Channel.UNLIMITED
    • したがって、デフォルトではランデブー・チャンネルが作成される
    • チャンネルを利用してメッセージをやりとりする例は以下の通り
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.*

fun main() = runBlocking<Unit> {
    val channel = Channel<String>()
    launch {
        channel.send("A1")
        channel.send("A2")
        log("A done")
    }
    launch {
        channel.send("B1")
        log("B done")
    }
    launch {
        repeat(3) {
            val x = channel.receive()
            log(x)
        }
    }
}

fun log(message: Any?) {
    println("[${Thread.currentThread().name}] $message")
}

// 出力結果は以下の通り
// [main @coroutine#4] A1
// [main @coroutine#4] B1
// [main @coroutine#2] A done
// [main @coroutine#3] B done
// [main @coroutine#4] A2
    • ランデブーチャンネルなのでA2より先にB1が送信されているのがわかる
    • これをChannel<String>(1)のようにバッファチャンネルにすると出力が以下のようになる:A1のsendがsuspendされないのですぐにA2のメッセージが送信されている
[main @coroutine#4] A1
[main @coroutine#4] A2
[main @coroutine#4] B1
[main @coroutine#2] A done
[main @coroutine#3] B done

 

[まとめ]

モブプログラミング・スタイルで、Introduction to Coroutines and Channelsハンズオンを進めた。

coroutineのチャンネルについて少し理解できた気がする!

 

今週はプルリクエストなし。