今週は第70回を開催しました!
引き続きKotlin Hands-onをすすめています。
前回の様子はコチラ↓
[第70回の様子]
2021/6/30に第70回を開催した。
今週はちょっと人が減って、自分をいれて3名での開催。
PC不調のため自分は当面ナビゲータ役で参加。
勉強会本編の内容としては、Kotlin/Native Concurrencyハンズオンの続きを実施した。第5章Backgroundの後半を完了し、第6章Debuggingを半分ほど進めた。
[学んだことや疑問点]
- Kotlin/Native Concurrency: 5. Background
- Workerを利用する際にfreeze()するのはローカル変数の場合はまだ理解しやすいが、意図した以上の範囲をキャプチャした場合に問題となりやすい
- 以下のようなcaptuerTooMuch()を実行してみる
class CountingModel{ var count = 0 fun increment(){ count++ background { saveToDb(count) } } private fun saveToDb(arg:Int){ //Do some db stuff println("Saving $arg to db") } } fun captureTooMuch(){ val model = CountingModel() model.increment() println("I have ${model.count}") model.increment() println("I have ${model.count}") //We won't get here }
- CountingModelオブジェクトを作成して、increment()を2回呼び出す:内部的にbackgroundでオブジェクトのプロパティをfreezeしているので、2回目の呼び出しはInvalidMutabilityExceptionが発生して失敗する
- わかりやすくするために、captureTooMuchAgain()を実行してみる
fun captureTooMuchAgain(){ val model = CountingModel() println("model is frozen ${model.isFrozen}") // false model.increment() println("model is frozen ${model.isFrozen}") // true }
- increment()を呼び出すとCountingModelのプロパティのcountがfreezeされるのだが、上記の結果からわかるように、親クラスのCountingModel自体もfreezeされている
- これを避けるためには、background呼び出しを関数化して、関数に渡す引数のみをfreezeするようにすればよい:以下のようにすると、argはメモリ上countプロパティとは別になるので、親クラスがfreezeされることもない
fun captureArgs(){ val model = CountingModelSafer() model.increment() println("I have ${model.count}") model.increment() println("I have ${model.count}") } class CountingModelSafer{ var count = 0 fun increment(){ count++ saveToDb(count) } private fun saveToDb(arg:Int) = background { println("Doing db stuff with $arg, in main $isMainThread") } }
- という内容を踏まえて、実験を行ってみた:saveToDbの引数を参照型にしたらどうなるか
- 用意したのは以下のようなコード。increment()を2回呼び出すのは変わらず
class CountingModelSafer { var state = InternalState(0) fun increment() { state.count++ saveToDb(state) } private fun saveToDb(arg: InternalState) = background { println("Doing db stuff with ${arg.count}, in main $isMainThread") } } data class InternalState(var count: Int)
- 上記の場合には、やはり我が友InvalidMutabilityExceptionが発生した
- しかし、以下の変更を加えると、問題なく動作した
fun increment() { state.count++ saveToDb(state.clone()) // シャロークローンで別オブジェクトに }
- シャロークローンだから、うまくいったが、プロパティに参照型が含まれる場合は?とさらに疑問が湧いたので実験してみる
- 用意したのは以下のようなクラス。(他の部分は省略、プルリクエストを参照。deepCopy()メソッドも実装されている)
data class InternalState(var count: Int, val state2: InternalState2)
- この場合はclone()してもダメで、clone時に追加でInternalState2をcloneすると、例外が発生しなくなった。
- 結論として、この手のメモリ管理はKotlinであまりやりたくないね、という話になった
- Kotlin/Native Concurrency: 6. Debugging
[まとめ]
モブプログラミング・スタイルで、Kotlin/Native Concurrencyハンズオンを進めた。
多少Native Concurrencyの理解が進んだ……気がする。
今週のプルリクエストはこちら。