1. 序章

このチュートリアルの目標は、コルーチンの世界でasyncおよびwithContextについての洞察を得ることです。 これらの2つの方法の使用方法、それらの類似点と相違点、および各方法の使用場所を確認するための短い道のりがあります。

2. Kotlinコルーチン

コルーチンは、リアクティブスタイルコーディングの頭痛の種なしに、シーケンシャルスタイルで流暢なAPIを使用して非同期コードを作成するための強力なツールです。 Kotlinは、言語の一部としてコルーチンを導入しました。 さらに、 kotlinx-coroutines-core は、コルーチンをより高度に使用するためのライブラリです。

コルーチンは、いくつかのCoroutineContext.Elementで構成されるコルーチンコンテキスト内で実行されます。 基本要素は、 CoroutineId、CoroutineName、CoroutineDispatcher、およびJobです。

CoroutineDispatcher は、コルーチンをエグゼキュータに割り当てます。 ディスパッチャは、イベントループやスレッドプールなどのさまざまなエグゼキュータに基づいて、ディスパッチにさまざまな戦略を使用できます。また、コルーチンを制限しないままにすることもできます。

CoroutineScope を使用すると、関連付けられたJobインスタンスによってコルーチンを管理できます。 コルーチンはにアクセスできます仕事 coroutineContext[ジョブ] 。 仕事コルーチンのライフサイクルを管理し、アクティブ、完了、キャンセルなどの状態を反映するためのインターフェイスです。

それでは、asyncwithContextとそれらの使用法を詳しく見てみましょう。

3. async-await とは何ですか?

async は、 CoroutineScope の拡張機能であり、新しいキャンセル可能なコルーチンを作成します。 したがって、コードブロックの将来の結果を保持するDeferredオブジェクトを返します。 Deferred#cancel を呼び出すことで、コルーチンをキャンセルできます。

非同期関数は構造化同時実行に従います。 したがって、失敗した場合は外部コルーチンをキャンセルします

Assertions.assertThrows(Exception::class.java) {
    runBlocking {
        kotlin.runCatching {
            async(Dispatchers.Default) {
                doTheTask(DELAY)
                throw Exception("Exception")
            }.await()
        }
    }
}

デフォルトでは、asyncコルーチンは作成されたとおりに実行を開始します。 ただし、 CoroutineStart argを渡すことで、この動作を変更できます。

async(Dispatchers.Default, CoroutineStart.LAZY)

さらに、互いに独立した複数のタスクがある場合は、asyncで開始して同時に実行することができます。 結合された結果が必要な場合は、各アイテムで Deferred#await を呼び出すことにより、すべてのコルーチンが完了するのを待つことができます。

val time = measureTimeMillis {
    val task1 = async { doTheTask(DELAY) }
    val task2 = async { doTheTask(DELAY) }
    task1.await()
    task2.await()
}
Assertions.assertTrue(time < DELAY * 2)

4. withContext とは何ですか?

withContext は、スコープ関数であり、新しいキャンセル可能なコルーチンを作成できます。 CoroutineContext argを渡すと、 withContext は親コンテキストとargをマージして、新しい CoroutineContextを作成し、はこのマージされたコンテキスト内でコルーチンを実行します。

また、ディスパッチャーをこの関数に渡して、渡されたディスパッチャーからのスレッドでブロックの実行が行われるようにすることもできます。 実行が完了すると、コントロールは前のディスパッチャに戻ります。

親ブロック内にwithContextの複数のブロックがある場合、それらのそれぞれの実行は親スレッドを一時停止しますが、それぞれが次々に順番に実行されます。

val time = measureTimeMillis {
    val dispatcher = newFixedThreadPoolContext(2, "withc")
    withContext(dispatcher) {
        doTheTask(DELAY)
    }
    withContext(dispatcher) {
        doTheTask(DELAY)
    }
}
Assertions.assertTrue(time >= DELAY * 2)

さらに、 withContext には、現在のコンテキストを使用するcoroutineScopeという拡張機能があります。 したがって、コンテキストスイッチは発生しません。

5. async-await対。 withContext

これら2つの機能に関する調査結果を要約してみましょう。

  • コルーチンの結果を収集する必要がある場合は、withContextまたはasyncを使用します
  • withContext は、asyncに続いてawaitと同じ機能を備えていますが、オーバーヘッドが少なくなっています。
  • 並列コード実行が必要な場合は、それらをいくつかの async ブロックに配置し、最後にそれらすべてをawaitします。
  • async では、async本体内のコードブロックの例外をキャッチする必要があります。 そうでなければ、それ 親スコープを終了できます

6. 結論

この記事では、コルーチンについて簡単に紹介しました。 次に、async-awaitwithContextの使用法を確認し、最後に、どちらを使用するかを決定しました。

いつものように、この記事の完全なコードは、GitHubから入手できます。