今週は第54回を開催しました!
引き続きKotlin Hands-onをすすめています。
前回の様子はコチラ↓
[第54回の様子]
2021/3/3に第54回を開催した。今週は普段どおり水曜日に実施。
参加メンバーは自分をいれて4名。今回も自分はナビゲータ役を担当した。
勉強会本編の内容としては、ひき続きIntroduction to Coroutines and Channelsハンズオンを進めた。第4章 Using suspend functionsの後半と、第5章 Concurrencyの前半部分を終えた。全体としては第9章まであるので、ようやく半分終わったという感じ。
3月中にこのハンズオン終わるかと思ったけど、このペースだと厳しそう....!まあのんびりやっていきますか。
[学んだことや疑問点]
- Introduction to Coroutines and Channels: 4. Using suspend functions
- suspend関数を利用する場合には、まず
launch関数でcoroutineを開始する必要がある launchが開始する計算はsuspend(中断)できる- GitHubのAPIを呼び出している間はsuspendされている、その間coroutineはスレッドから削除されてメモリ上に保持された状態になる
- これにより、スレッドが他の処理(GUIの描画など)に利用できるようになる
- JVM引数に
-Dkotlinx.coroutines.debugオプションを追加すると、SLF4Jのログ出力時に、スレッド名に加えて以下のようにcoroutine名も出力されるようになる(@coroutine#1の部分がそれ)
2538 [AWT-EventQueue-0 @coroutine#1] INFO Contributors - kotlin: loaded 30 repos
- @coroutine#1はあくまでUIメインスレッド(AWT-EventQueue-0)で実行されていることがわかる
- Introduction to Coroutines and Channels: 5. Concurrency
- coroutineはスレッドと比べて非常に安価である
- coroutineを開始するにはcoroutine builderを利用する必要がある
- 既に紹介した
launchの他に、asyncやrunBlockingなどがある launchの戻り値の型はJobで、asyncの戻り値の型はJobを拡張したDeffered- いずれも
Job.join()でcoroutineの終了を待つことができる - さらに
Defferedの場合は以下のようにasyncで実行される計算の戻り値の型を指定でき、Deffered.await()で計算結果を取得することができる
import kotlinx.coroutines.*
fun main() = runBlocking {
val deferred: Deferred<Int> = async {
loadData()
}
println("waiting...")
println(deferred.await())
}
suspend fun loadData(): Int {
println("loading...")
delay(1000L)
println("loaded!")
return 42
}
runBlockingは上記のコードのようにmain関数で使うか、テストコードで利用する場合が多いらしい- また、複数の
Defferedを扱う場合は以下のようにawaitAll()を利用できる
import kotlinx.coroutines.*
fun main() = runBlocking {
val deferreds: List<Deferred<Int>> = (1..3).map {
async {
delay(1000L * it)
println("Loading $it")
it
}
}
val sum = deferreds.awaitAll().sum()
println("$sum")
}
[まとめ]
モブプログラミング・スタイルで、Introduction to Coroutines and Channelsハンズオンを進めた。
coroutineとスレッドとの関係が少し理解できた!
coroutineがスレッドに再割り当てされる際の実装がどうなっているのかとか、気になる部分はまだあるけど……。
このあたりをしっかり理解できれば、2020年のアドベントカレンダーで実装したHTTPサーバーを改良できそうな気がする。
今週も進捗があったものの、プルリクエストはなし。