Kotlin 1.3: Coroutine の基本 (2 of 2)


Kotlin 1.3: Coroutine の基本 (2 of 1) からの続きです。

コルーチンを別の関数にする

launch { ... } の中身を別の関数に展開してみます。 “Extract function” リファクタリング を実行すると suspend 修飾子 のついた新しい関数を作ることができます。 サスペンディングファンクションは通常の関数と同じようにコルーチンの中で使うことができます。 そして、他のサスペンディングファンクションを呼び出すことができ、またコルーチンの実行を停止させることができます。

外部化された関数が実行中スコープでコルーチンビルダを使っていたらどうなるでしょうか。 その場合は suspend 修飾子 のみでは不十分です。 CoroutineScopedoWorld 拡張関数 をつけるのがひとつの方法ですが、 万能ではありません。 慣用的な解決策は、明示的なCoroutineScope を目的の関数を含むクラスのフィールドとして実装するか、 外部クラスが CoroutineScope を実装するときに暗黙的に指定することです。 最後の手段として、 CoroutineScope (coroutineContext) を使用できますが、 このメソッドが実行されるスコープを制御できなくなったため、このようなアプローチは構造的に安全ではありません。 このビルダを使用できるのはプライベートAPIだけです。

コルーチンは軽量である

次のコードを実行すると、およそ1秒後に100,000個のピリオドが出力されます。

もう少し簡単にして比較してみます。 100,000個のピリオドを出力する3パターンのコードを実行してみます。

さて、どれが一番早いでしょうか。 上から順に高速に出力します。 コルーチンの方がスレッドより速いことがわかります。

runBlocking を使わないパターンも試してみます。

ループで出力しているものが一番早いです。 2番目のループでスレッドを作成するものは、途中でエラーメッセージが出ます。

デーモンスレッドのようなグローバルコルーチン

Java のスレッドにはユーザースレッドとデーモンスレッドの2種類があります。 デーモンスレッドはプログラムが終了する際に停止を待たず、 全てのユーザースレッドが終了すると自動的に終了します。

このコードは、 十分な時間があれば、 1000回 文字列を出力します。 しかし、 delay(1300L) で 1500ミリ秒 経過後には終了してしまうので、 出力は3回しか行われません。

1000行出力してから終了したいのであれば、 runBlocking で生成されたコルーチンスコープの launch メソッド を使用するのがひとつの方法です。

これと同じ結果になりますが、 GlobalScope.launch で返される Jobjoin メソッド を 実行する方法もあります。 join を実行することで、 外側のスコープを、該当のコルーチンが終了するまで停止させられます。