web-dev-qa-db-ja.com

タスク<T>が完了するまでコルーチンを一時停止する正しい方法

私は最近Kotlinコルーチンを使い始めました。多くのGoogleのライブラリを使用しているため、ほとんどのジョブは Task クラス内で行われます

現在、この拡張機能を使用してコルーチンを一時停止しています

suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
    task.addOnCompleteListener { task ->
        if (task.isSuccessful) {
            continuation.resume(task.result)
        } else {
            continuation.resumeWithException(task.exception!!)
        }
    }
}

しかし最近、私はこのような使い方を見てきました

suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
    try {
        val result = Tasks.await(task)
        continuation.resume(result)
    } catch (e: Exception) {
        continuation.resumeWithException(e)
    }
}

違いはありますか、どちらが正しいですか?

UPD:2番目の例が機能しない、idkの理由

11
Dima Rostopira

_suspendCoroutine { ... }_に渡されるコードのブロックは、それが呼び出されているスレッドをブロックしてはならず、コルーチンを中断することができます。このようにして、実際のスレッドを他のタスクに使用できます。これは、Kotlinコルーチンをスケーリングして、単一のUIスレッドでも複数のコルーチンを実行できるようにする重要な機能です。

最初の例は _task.addOnCompleteListener_(docsを参照) を呼び出すため、正しく実行されます(リスナーを追加してすぐに戻るだけです。そのため、最初の例は正しく機能します。

2番目の例では、 Tasks.await(task)(docsを参照) を使用しています。これは、呼び出されているスレッドをblocksし、タスクが完了しているため、コルーチンを適切に一時停止できません。

14
Roman Elizarov

2番目の例は、より伝統的なブロッキング関数です。

典型的なJavaマルチスレッドの世界では、そのような関数を使用して、結果が返されるまでスレッドをブロックします。

また、典型的なJavaには、Reactiveプログラミングと呼ばれる新しいパラダイムがあります。これにより、結果が返されるまでスレッドをブロックしないようにすることができます。結果を待つだけでなく、結果が到着するたびに実行されるファーストクラスの関数を提供します。そうすれば、一時停止中のスレッドは残りません。代わりに、長い操作を実行しているスレッドに追加された新しいイベントがあり、操作が完了すると、そのイベントが発生します。

ブロッキング:

Thread one:  --* . . . ./---
Thread two:     \------*

反応性:

Thread one: --*   (now free)
Thread two:    \-------*---

リアクティブプログラミングを研究すれば、より深く掘り下げた説明が見つかります。

あなたの質問に戻りますが、一見同じことを行うこれら2つの方法は、Javaの世界からのパラダイムシフトの結果です。もちろん、ここでコルーチンを使用しているので、そのブロッキングの問題を心配する必要はありませんまったく;言語はsuspendingブロックする代わりに、言語レベルでは、反応的にプログラミングすることの多くの利点が解消されます。

ただし、そのパラダイムには依然としてメリットがあるため、他の場所ほど厳密に必要ではない場合でも、そのパターンで作業を続ける人々を見るのは理にかなっています。

1
forresthopkinsa