私はコトリンコルーチンを読んでおり、それがsuspend
関数に基づいていることを知っています。しかし、suspend
はどういう意味ですか?
コルーチンまたは機能が停止しますか?
から https://kotlinlang.org/docs/reference/coroutines.html
基本的に、コルーチンはスレッドをブロックせずに中断できる計算です
人々はしばしば「機能を停止する」と言うのを聞きました。しかし、機能が終了するのを待っているために中断されるのはコルーチンだと思いますか? 「サスペンド」は通常、「操作の停止」を意味します。この場合、コルーチンはアイドル状態です。
????コルーチンが中断されていると言うべきですか?
どのコルーチンが中断されますか?
から https://kotlinlang.org/docs/reference/coroutines.html
類推を続けるために、await()は、何らかの計算が完了するまでコルーチンを中断し、その結果を返す中断関数(したがって、非同期{}ブロック内からも呼び出し可能)になります。
async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}
???? 「なんらかの計算が完了するまでコルーチンを一時停止する」と書かれていますが、コルーチンは軽量スレッドのようなものです。コルーチンが中断された場合、どのように計算を実行できますか?
await
がcomputation
で呼び出されるので、async
を返すのはDeferred
である可能性があります。つまり、別のコルーチンを開始できます
fun computation(): Deferred<Boolean> {
return async {
true
}
}
????引用文は、コルーチンを一時停止すると言います。 suspend
外側のasync
コルーチン、またはsuspend
内側のcomputation
コルーチンを意味しますか?
suspend
は、外側のasync
コルーチンが内側のawait
コルーチンが終了するのを待っている間(computation
)、それ(外側のasync
コルーチン)がアイドル状態であることを意味しますか(したがって、名前は一時停止し)スレッドをスレッドプールに返し、子computation
コルーチンが終了すると、(外側のasync
コルーチン)が起動し、プールから別のスレッドを取得して続行しますか?
スレッドに言及した理由は、 https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
コルーチンが待機している間にスレッドがプールに戻され、待機が完了すると、プール内の空きスレッドでコルーチンが再開します
サスペンド機能はすべてのコルーチンの中心にあります。一時停止機能は、一時停止して後で再開できる機能です。長時間実行される操作を実行し、ブロックせずに完了するまで待つことができます。
一時停止機能の構文は、一時停止キーワードの追加を除いて、通常の機能の構文と似ています。パラメータを受け取り、戻り値の型を持つことができます。ただし、一時停止機能は、別の一時停止機能またはコルーチン内でのみ呼び出すことができます。
suspend fun backgroundTask(param: Int): Int {
// long running operation
}
内部では、サスペンド関数はコンパイラーによってsuspendキーワードのない別の関数に変換されます。このキーワードはContinuation型の追加パラメーターを取ります。たとえば、上記の関数は、コンパイラによって次のように変換されます。
fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
// long running operation
}
Continuationは、関数が中断されている間にエラーが発生した場合、戻り値または例外を使用してコルーチンを再開するために呼び出される2つの関数を含むインターフェイスです。
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
コルーチンを中断することの正確な意味を理解するには、次のコードを実行することをお勧めします。
import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
var continuation: Continuation<Int>? = null
fun main() = runBlocking {
launch(Unconfined) {
val a = a()
println("Result is $a")
}
10.downTo(0).forEach {
continuation!!.resume(it)
}
}
suspend fun a(): Int {
return b()
}
suspend fun b(): Int {
while (true) {
val i = suspendCoroutine<Int> { cont -> continuation = cont }
if (i == 0) {
return 0
}
}
}
Unconfined
コルーチンディスパッチャは、コルーチンディスパッチの魔法を排除し、裸のコルーチンに直接集中できるようにします。
launch
ブロック内のコードは、launch
呼び出しの一部として、現在のスレッドですぐに実行を開始します。起こることは次のとおりです。
val a = a()
を評価するb()
にチェーンし、suspendCoroutine
に到達します。b()
は、suspendCoroutine
に渡されたブロックを実行し、特別なCOROUTINE_SUSPENDED
値を返します。この値はKotlinプログラミングモデルでは確認できませんが、コンパイルされたJavaメソッドはそれを実行します。a()
は、この戻り値を見て、それ自体も返します。launch
ブロックも同じことを行い、launch
呼び出しの後の行に制御が戻ります:10.downTo(0)...
この時点で、launch
ブロック内のコードとfun main
コードが同時に実行されている場合と同じ効果があることに注意してください。これはすべて、単一のネイティブスレッドで発生しているため、launch
ブロックは「中断」されています。
これで、forEach
ループコード内で、プログラムはb()
関数が書き込んだcontinuation
と10
の値を持つresumes
を読み取ります。 resume()
は、渡された値でsuspendCoroutine
呼び出しが返されるように実装されます。そのため、突然b()
を実行していることに気付くでしょう。 resume()
に渡した値はi
に割り当てられ、0
に対してチェックされます。ゼロでない場合、while (true)
ループはb()
内で継続し、再びsuspendCoroutine
に到達します。この時点でresume()
呼び出しが戻り、今度はforEach()
の別のループステップを実行します。これは、最後に0
で再開するまで続き、その後println
ステートメントが実行され、プログラムが完了します。
上記の分析により、「コルーチンの一時停止」とは、コントロールを最も内側のlaunch
呼び出し(または、より一般的にはコルーチンビルダー)に戻すことを意味するという重要な直観が得られます。再開後にコルーチンが再び中断されると、resume()
呼び出しは終了し、resume()
の呼び出し元に制御が戻ります。
コルーチンディスパッチャの存在は、ほとんどのコードがすぐに別のスレッドにコードを送信するため、この推論の明確性を低下させます。その場合、上記の話は他のスレッドで発生し、コルーチンディスパッチャはcontinuation
オブジェクトも管理するため、戻り値が利用可能になったときに再開できます。
コルーチンまたは機能が中断されますか?
サスペンドの呼び出しing function suspend sコルーチン。現在のスレッドが別のコルーチンの実行を開始できることを意味します。したがって、coroutineは、関数ではなく中断されていると言われます。
しかし、技術的には、その時点で関数は別のコルーチンによって実行されないため、関数とコルーチンの両方が停止すると言うことができますが、ここでは髪を分割しています。
どのコルーチンが中断されますか?
外側のasync
はコルーチンを開始します。 computation()
を呼び出すと、内側のasync
は2番目のコルーチンを開始します。その後、await()
の呼び出しは、outerasync
コルーチンの実行を、innerasync
のコルーチンは終了しました。
単一のスレッドでもそれを見ることができます。スレッドは外側のasync
の先頭を実行し、computation()
を呼び出して内側のasync
に到達します。この時点で、内部非同期の本体はスキップされ、スレッドはawait()
に達するまで外部async
の実行を続けます。 await
は一時停止機能であるため、await()
は「一時停止ポイント」です。これは、外側のコルーチンが中断され、スレッドが内側のコルーチンの実行を開始することを意味します。完了すると、外側のasync
の終わりを実行するために戻ります。
一時停止とは、外部非同期コルーチンが内部計算コルーチンの終了を待機している間(待機)、外部非同期コルーチンがアイドル状態(一時停止)でスレッドプールにスレッドを返し、子計算コルーチンが終了したことを意味します、それ(外部非同期コルーチン)が起動し、プールから別のスレッドを取得して続行しますか?
はい、正確に。
suspend
を理解する最善の方法は、this
キーワードとcoroutineContext
プロパティを類推することです。
Kotlin関数は、ローカルまたはグローバルとして宣言できます。ローカル関数はthis
キーワードに魔法のようにアクセスできますが、グローバル関数にはアクセスできません。
Kotlin関数は、suspend
またはブロッキングとして宣言できます。 suspend
関数はcoroutineContext
プロパティに魔法のようにアクセスできますが、ブロック関数にはアクセスできません。
事は:coroutineContext
property 「通常の」プロパティのように宣言されている Kotlin stdlibであるが、この宣言はドキュメント/ナビゲーションの目的のためのスタブにすぎない。実際、coroutineContext
は ビルトイン組み込みプロパティ です。これは、言語キーワードを認識しているように、コンパイラがこのプロパティをマジックで認識していることを意味します。
this
キーワードがローカル関数に対して行うことは、coroutineContext
プロパティがsuspend
関数に対して行うことです。これは、現在の実行コンテキストへのアクセスを提供します。 2番目のケース。
したがって、suspend
プロパティへのアクセスを取得するには、coroutineContext
が必要です-現在実行されているコルーチンコンテキストのインスタンス
継続の概念の簡単な例を挙げたいと思います。これは、サスペンド機能がフリーズ/サスペンドしてから続行/再開できることです。スレッドとセマフォの観点からコルーチンを考えるのをやめます。継続とコールバックフックの観点から考えてください。
明確にするために、疑わしい機能を使用して、コートを一時停止することができます。これを調査しましょう:
Androidでは、たとえば次のようにできます。
var TAG = "myTAG:"
fun myMethod() {
viewModelScope.launch(Dispatchers.Default) {
for (i in 10..15) {
if (i == 10) { //on first iteration, we will completely FREEZE this coroutine (just for loop here gets 'suspended`)
println("$TAG im a tired coroutine - let someone else print the numbers async. i'll suspend until your done")
freezePleaseIAmDoingHeavyWork()
} else
println("$TAG $i")
}
}
//this area is not suspended, you can continue doing work
}
suspend fun freezePleaseIAmDoingHeavyWork() {
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)//delay pauses coroutine, NOT the thread. use Thread.sleep if you want to pause a thread.
}
println("$TAG phwww finished printing those numbers async now im tired, thank you for freezing, you may resume")
}
}
}
次を印刷します。
I: myTAG: my coroutine is frozen but i can carry on to do other things
I: myTAG: im a tired coroutine - let someone else print the numbers async. i'll suspend until your done
I: myTAG: 1
I: myTAG: 2
I: myTAG: 3
I: myTAG: 4
I: myTAG: 5
I: myTAG: 6
I: myTAG: 7
I: myTAG: 8
I: myTAG: 9
I: myTAG: 10
I: myTAG: phwww finished printing those numbers async now im tired, thank you for freezing, you may resume
I: myTAG: 11
I: myTAG: 12
I: myTAG: 13
I: myTAG: 14
I: myTAG: 15
このように機能すると想像してください:
そのため、起動した現在の機能は停止せず、コルーチンが継続している間だけ停止します。スレッドは、サスペンド機能を実行しても一時停止しません。
このサイトが役に立ちます あなたはまっすぐに物事を出し、私の参考になります。
イテレーションの途中で何かクールなことをして、サスペンド機能をフリーズさせます。後でonResumeで再開します。
継続と呼ばれる変数を保存し、コルーチンの継続オブジェクトを適切にロードします。
var continuation: CancellableContinuation<String>? = null
suspend fun freezeHere() = suspendCancellableCoroutine<String> {
continuation = it
}
fun unFreeze(){
continuation?.resume("im resuming") {}
}
ここで、サスペンド関数に戻り、反復の途中でフリーズさせます。
suspend fun freezePleaseIAmDoingHeavyWork() {
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)
if(i == 3)
freezeHere() //dead pause, do not go any further
}
}
}
}
次に、onResumeのような他の場所(たとえば):
override fun onResume() {
super.onResume()
unFreeze()
}
ループが継続します。一時停止関数をいつでもフリーズして、しばらく経ってから再開できることを知っているのは、とてもすてきです。 channels を調べることもできます