Kotlinコルーチンは複雑ではない、私はあなたがその理由を管理するのに役立ちます

コルーチンコルーチン

要約すると、最近Kotlin何か、と私は、これは簡単に言っているのではない。下手に書かれたのエッセイ前に、私は書き直すことにしましたので。コルーチンを見つけました
。この部分では、ほとんどの場合、繰り返し研究し、様々な文書や公式サイトのチュートリアルブログ財団はまた、最も重要なコンテンツである、と読んで理解することができ、また白に努めています。

コルーチンのコンセプト

コルーチンノンプリエンプティブマルチタスクをサポートするために、(コルーチン)、コンピュータ・プログラム・コンポーネント、中断と再開タスクの実行を可能にすることによって、。(参照ウィキ)。

コルーチン主に非同期、非ブロックコードへ。この概念はサポートされているなどKotlin、複数の言語ゴー、パイソン、に特有のものではありません。

Kotlinコルーチン

Kotlinコルーチンは、非同期および非ブロックタスクを行うには、主な利点は、良いコードの可読性、ないコールバック関数です。(同期コードのような一見非同期コードを書くためにコルーチンを使用しました。)

言語レベルでのコルーチンのためKotlinのサポートは、標準ライブラリには、最小限のAPIを提供し、その後、多くの機能は、ライブラリへのプロキシです。

Kotlinは追加suspendのキーワードとして。
asyncそして、await標準ライブラリの一部ではない、Kotlinのキーワードをありません。

先物と約束比べると、中kotlin suspending function概念は、非同期操作のためのより安全でよりエラーを起こしやすい抽象化を提供します。

kotlinx.coroutinesコルーチンライブラリがあり、そのコア機能を使用するためには、プロジェクトが増加する必要がkotlinx-coroutines-core信頼を。

コルーチンの基本:何であるか最後にコルーチン?

公式のデモの最初のセクション:

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch


fun main() {
    GlobalScope.launch { // launch a new coroutine in background and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello,") // main thread continues while coroutine is delayed
    Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}

このコードの出力は:
こんにちは出力し、1秒の遅延の後、世界を印刷します。

このコードの解釈:

launch計算開始は、この計算は、コルーチンの実行が完了すると、演算処理において、底糸の放出は、(再開)を再開する、(休止)に懸濁されています。

この計算は、コルーチン(コルーチン)と呼ばれている懸濁させることができる。我々は単純に言うことができるのでlaunch、新たなコルーチンの始まり。

メインスレッドのニーズは、コルーチンの終了を待つ最後の行をコメントアウトすることに注意Thread.sleep(2000L)のみ、こんにちは何の世界を印刷していません、。

プロセスとスレッドの関係研究所

コルーチン(コルーチン)は、軽量スレッドとして理解することができる。より多くのコルーチンがお互いを待って、並列に実行することができ、相互に通信する。コルーチンは非常に軽量でコルーチンとスレッド間の最大の違い(安い)、私たちは何千を作成することができます十かかわらず、パフォーマンスのコルーチンの何千もの。

コルーチンはスレッド上で実行中の操作は、操作は、中断されたスレッドから取り外し、メモリに記憶させることができることを意味し、懸濁させることができる懸濁させることができる。この場合、スレッドは他のことを行うために自由になります。準備を計算する場合ハーシーが続く、それがスレッドを返します(必ずしも同じスレッドを持っていません)。

デフォルトでは、コルーチンは、共有スレッドプールで実行、スレッドがまだそこにある、唯一つのスレッドは、必要に応じて、複数のコルーチンので、あまりにも多くのスレッドを実行することができます。

デバッギング

プラス上記のコード内のスレッドの名前:

fun main() {
    GlobalScope.launch {
        // launch a new coroutine in background and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World! + ${Thread.currentThread().name}") // print after delay
    }
    println("Hello, + ${Thread.currentThread().name}") // main thread continues while coroutine is delayed
    Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}

VMオプションは、編集構成IDEで提供されてもよい:-Dkotlinx.coroutines.debugプログラムを実行し、ログで実行しているコルーチン情報コードを出力します。

Hello, + main
World! + DefaultDispatcher-worker-1 @coroutine#1

サスペンド機能

上記の例ではdelay方法があるsuspend function
delay()そして、Thread.sleep()違いがある:delay()。コルーチン方法はスレッドをブロックせずに遅延させることができる(これは、スレッドをブロックしないだけコルーチン自体を中断) し、Thread.sleep()現在のスレッドがブロックされました。

だから、スコープコルーチンが中断されていることを意味し、サスペンドが、ブロックされない範囲のコルーチン外の現在のスレッドコード。

場合はGlobalScope.launch交換しthread、遅延方法は、赤いラインエラーの下に表示されます。

Suspend functions are only allowed to be called from a coroutine or another suspend function

この方法は、唯一のコルーチンが中断または別の方法中断呼び出すことができます。

コルーチン待機中に、スレッドは、コルーチン待ちの終わりには、コルーチンがフリースレッドプールに復元されるスレッドプールに戻ります。(スレッドはコルーチンを待っている間にプールに戻され、いつれます待機は、プールの空きスレッド上のコルーチンを再開して行われます。)

スタートコルーチン

以下の方法で主に使用される新しいコルーチンを開始します。

  • launch
  • async
  • runBlocking

彼らは呼ばれているcoroutine builders別のライブラリをより構築されているくらいに定義することができます。

runBlocking:世界での接続を遮断し、非ブロック

runBlockingブロッキングと非ブロッキングの世界を接続するために使用されます。

runBlockingそれは主に、main関数で使用されるか、またはテストで使用されているので、リンク機能として、現在のスレッドコルーチンブロックを作成することができます。

上記の例の一例は、以下のように書き換えることができます。

fun main() = runBlocking<Unit> {
    // start main coroutine
    GlobalScope.launch {
        // launch a new coroutine in background and continue
        delay(1000L)
        println("World! + ${Thread.currentThread().name}")
    }
    println("Hello, + ${Thread.currentThread().name}") // main coroutine continues here immediately
    delay(2000L) // delaying for 2 seconds to keep JVM alive
}

最後に、もはや使用中Thread.sleep()、使用しないdelay()ことを。
手順の出力を:

Hello, + main @coroutine#1
World! + DefaultDispatcher-worker-1 @coroutine#2

打ち上げ:仕事への復帰

コルーチンの終了を待つ上記遅延時間の例としては、良い方法ではありません。

launch戻りJobコルーチンの代わりに、我々は使用することができ、明示的にこのコルーチンの終了を待つ方法を:Jobjoin()

fun main() = runBlocking {
    val job = GlobalScope.launch {
        // launch a new coroutine and keep a reference to its Job
        delay(1000L)
        println("World! + ${Thread.currentThread().name}")
    }
    println("Hello, + ${Thread.currentThread().name}")
    job.join() // wait until child coroutine completes
}

出力は上記と同じです。

Jobもう一つの重要な用途はあるcancel()不要になったタスクコルーチンをキャンセルします。

非同期は:コルーチンから値を返します。

asyncオープンスレッド、リターンはDeferred<T>Deferred<T>Jobあり、サブクラスawait()コルーチンの結果を返す関数は。

await()また、機能だけコルーチンの中に呼び出すことができます一時停止。

fun main() = runBlocking {
    // @coroutine#1
    println(Thread.currentThread().name)
    val deferred: Deferred<Int> = async {
        // @coroutine#2
        loadData()
    }
    println("waiting..." + Thread.currentThread().name)
    println(deferred.await()) // suspend @coroutine#1
}

suspend fun loadData(): Int {
    println("loading..." + Thread.currentThread().name)
    delay(1000L) // suspend @coroutine#2
    println("loaded!" + Thread.currentThread().name)
    return 42
}

結果:

main @coroutine#1
waiting...main @coroutine#1
loading...main @coroutine#2
loaded!main @coroutine#2
42

コンテキスト、ディスパッチャ和のスコープ

見てくださいlaunchメソッド宣言:

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
...
}

概念のいくつかは、我々は見つけることがあります。

コルーチンは、常にコンテキストファイル名を指定して実行で、型がインターフェースであるCoroutineContext。コルーチンコンテキストはさまざまな要素が含まれているインデックスのセットです、重要な要素があるJobとディスパッチャが。Job協会のプロセスを代表して、その後、ディスパッチャは何をするのですか?

Builderのビルドはコルーチンをコルーチン:launchasync、あるCoroutineScope表示する拡張メソッドの種類CoroutineScopeが含まインタフェース、CoroutineContextその中のいずれかの役割はそこにあるものの範囲への参照を??。

ここでは、これらの質問に答える必要があります。

ディスパッチャスレッド

コンテキストは、CoroutineDispatcherコルーチン実行中のスレッドがスレッド、スレッドプールを、割り当てられたりないことができるものを指定することができます。

例を見てください:

fun main() = runBlocking<Unit> {
    launch {
        // context of the parent, main runBlocking coroutine
        println("main runBlocking      : I'm working in thread ${Thread.currentThread().name}")
    }
    launch(Dispatchers.Unconfined) {
        // not confined -- will work with main thread
        println("Unconfined            : I'm working in thread ${Thread.currentThread().name}")
    }
    launch(Dispatchers.Default) {
        // will get dispatched to DefaultDispatcher
        println("Default               : I'm working in thread ${Thread.currentThread().name}")
    }
    launch(newSingleThreadContext("MyOwnThread")) {
        // will get its own new thread
        println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
    }
}

プリントアウトした後に実行されています:

Unconfined            : I'm working in thread main
Default               : I'm working in thread DefaultDispatcher-worker-1
newSingleThreadContext: I'm working in thread MyOwnThread
main runBlocking      : I'm working in thread main

APIは、いくつかのオプションを提供しています:

  • Dispatchers.Default使用サイズCPUコアの数によって決定されるが、それでもシングルコアは、2つのスレッドを有しているJVMの代わりにスレッドプールを共有した。典型的には、このようなソートや複雑な計算として、CPU集約型の仕事のために使用しました。
  • Dispatchers.Main指定UIのアップデートに関連するものを作るために使用されるメインスレッド、。(依存を追加する必要がある、などをkotlinx-coroutines-android。)我々は新しいコルーチンを開始し、メインスレッド上にある場合は、メインスレッドがビジー状態である、コルーチンは、場合にのみ中断されます実行スレッドを再開する無料となります。
  • Dispatchers.IO:ファイルを読み書きするために、ネットワークや仕事のスレッドプールのオンデマンド作成。
  • Dispatchers.Unconfined:あなたは特別なディスパッチャある特定のスレッドを、指定しないでください。

あなたが明示的にディスパッチャを指定しない場合は、コルーチンは、(ディスパッチャが含まれている)、それが起動するコンテキストのスコープを継承します。

実際には、呼び出し元のコンテキストによって決定の外部ディスパッチャースコープを使用することをお勧めします。これもテストを容易にします。

newSingleThreadContextコルーチンを実行するスレッドを作成し、専用のスレッドが実用的なアプリケーションやストレージの再利用にリリースされる、高価な資源とみなされています。

スイッチングスレッドも使用することができwithContext、あなたが指定したコルーチンコンテキストでコードを実行することができます最終的な結果が返されるまで、中断されます。
もう一つの方法は、その後、新しいコルーチンを開始することでjoin明示的に一時停止を待ちます。

AndroidのUIは、このアプリケーションでは、より一般的な方法は、トップコルーチンを使用することであるCoroutineDispatchers.Main必要性は、他のスレッドの時間に何かをして、明示的に別のディスパッチャを指定するとき、。

スコープは何ですか?

ときlaunchasyncまたはrunBlocking新しいコルーチン時間を開いて、彼らは自動的に対応するスコープを作成します。これらの方法の全てはラムダのパラメータデフォルトの受信機タイプの受信機を持っていますCoroutineScope

IDEには、プロンプトが表示されますthis: CoroutineScope

launch { /* this: CoroutineScope */
}

我々ときrunBlockinglaunchまたはasync、新しいコルーチン時間を作成中括弧は、それが自動的にこのスコープで作成されます。

fun main() = runBlocking {
    /* this: CoroutineScope */
    launch { /* ... */ }
    // the same as:
    this.launch { /* ... */ }
}

以来launch拡張メソッド、上記の例では、デフォルトの受信機であるthis
この例ではlaunch開始されている外部コルーチンのコルーチンが(呼ばれrunBlockingた子のコルーチンを開始)。スコープを渡すことによって、この関係は「親子」 :スコープの親で子供のスタート。

親子関係のコルーチン:

  • コルーチンが別のコルーチンの範囲で起動すると、自動的にそのコンテキストを継承し、新しいジョブコルーチンは、親ジョブのコルーチンの子として機能します。

だから、スコープに関して二つの重要な知識のポイントがあります。

  • 我々は常にで、コルーチンを開くCoroutineScopeレーン。
  • 別のコルーチン間の親子関係や構造を管理するためのスコープ。

親子関係のコルーチンは、以下の2つの特性があります。

  • 親コルーチンが解除され、すべてのサブコルーチンがキャンセルされています。
  • 父は常に、すべてのサブコルーチンの終了を待っていることがコルーチン。

それはあなたが新しいスコープを作成するために、コルーチンを開始することができない、ことは注目に値するが、スコープのファクトリメソッドを作成するために使用することができます:MainScope()CoroutineScope()

coroutineScope()この方法はまた、スコープを作成することができます。私たちはサスペンド機能内に新しいコルーチンを開始するための構造化された方法を必要とするとき、私たちは新しいスコープを作成し、自動的にサスペンド機能が呼び出されるの範囲外の子になります。

上記の親子関係、さらになし親コルーチン、サブコルーチンのすべてを管理するのスコープに抽象化することができます。

私たちのアプリケーションは、独自のライフサイクルを持つオブジェクトが存在しますが、オブジェクトは、そのようないくつかのコルーチンを行うために開始したAndroidアプリケーションの活動、などコルーチン、ない場合は適用範囲は?それの実用化に問題を解決します必要性の活動は、我々が使用することができます回避メモリリークへのすべてのコルーチンをキャンセルしたときに、非同期操作などの更新データは、破壊された。CoroutineScopeそれを行うには:作成CoroutineScope達成するための活動をライフサイクルの標的結合及び活性を、または許可しますCoroutineScopeインタフェース。

そのため、役割の主な範囲は、すべてのコルーチンを記録することで、それらを取り消すことができます。

A CoroutineScope keeps track of all your coroutines, and it can cancel all of the coroutines started in it.

構造化された同時実行

このようなメカニズムは、「構造化の同時実行」として知られている構造化されたプロセスを、組織的範囲協会を使用します。
利点は以下のとおりです。

  • 子コルーチンサブコルーチン寿命とスコープのバインディングのために自動的に責任範囲。
  • スコープは、自動的にすべてのサブコルーチンをキャンセルします。
  • スコープは、自動的にすべてのサブコルーチンの終了を待つ。親共同チェンスコープやバインディング、親コルーチンはサブアソシエーションプロセスが完了したすべてのスコープを待つことになる場合。

構成された本並行モードでは:必要なときに我々は、トップレベルのアソシエーションプロセスを作成すると、プライマリコンテキストを指定することができ、ネストされたすべてのコルーチンは自動的にこの文脈継承し、我々は唯一の変更を行うことができます。

GlobalScope:デーモン

GlobalScope唯一の制限を適用することにより、コルーチンは独立している彼らの生活を始める。ことGlobalScope、コルーチンに親を開始していない、それが起動されたときのスコープの外に位置しており、それは問題ではありません。

launch(Dispatchers.Default) { ... }そして、GlobalScope.launch { ... }ディスパッチャの使用は同じです。

GlobalScopeアクティブのままコルーチンプロセスを起動していない。JVM一般的には他のスレッドが閉じられません見つかったかのように彼らは、デーモンスレッド(デーモンスレッド)のようなものです。

参照

サードパーティのブログ:

おすすめ

転載: www.cnblogs.com/mengdd/p/kotlin-coroutines-basics.html