Kotlinコルーチンは「前に起こる」保証をしますか?


10

Kotlinコルーチンは「前に起こる」保証を提供しますか?

たとえばmutableVar、この場合、他のスレッドへの(潜在的に)書き込みとその後の読み取りの間に「前に起こる」保証があります。

suspend fun doSomething() {
    var mutableVar = 0
    withContext(Dispatchers.IO) {
        mutableVar = 1
    }
    System.out.println("value: $mutableVar")
}

編集:

たぶん、追加の例は、(変更可能性を除いて)Kotlinっぽいので、問題をより明確にするでしょう。このコードはスレッドセーフですか?

suspend fun doSomething() {
    var data = withContext(Dispatchers.IO) {
        Data(1)
    }
    System.out.println("value: ${data.data}")
}

private data class Data(var data: Int)

JVMで実行する場合、KotlinはJavaと同じメモリモデルを使用することに注意してください。
Slaw

1
@Slaw、私はそれを知っています。ただし、内部では多くの魔法が実行されています。したがって、コルーチンから取得する前に発生する保証があるのか​​、それともすべて私にあるのかを理解したいと思います。
Vasiliy

どちらかと言えば、2番目の例は、さらに単純なシナリオをwithContext示しています。最初の例は最初に作成し、内withContextで変更し、その後にを読み取りますwithContext。したがって、最初の例では、より多くのスレッドセーフ機能を実行します。
Marko Topolnik

...そしてどちらの例も、発生前の最も簡単な「プログラムの順序」の側面のみを実行します。ここでは、基礎となるJVMではなく、コルーチンのレベルについて話しています。つまり、基本的には、Kotlinコルーチンが非常に壊れているため、プログラムの順序が発生する前に提供されていないかどうかを尋ねています。
Marko Topolnik

1
@MarkoTopolnik、私が間違っている場合は修正してください。ただし、JLSは、同じスレッドでの実行に対して「プログラムの順序が発生する前に」のみ保証します。現在、コルーチンを使用すると、コードはシーケンシャルに見えますが、実際には、コードを異なるスレッドにオフロードするいくつかの機構があります。「これは、私が時間を無駄にしないことを保証する基本的な保証です」(別のコメントから)の点を理解しましたが、厳密な回答を得るためにこの質問をしました。私が書いた例はスレッドセーフであると確信していますが、その理由を理解したいと思います。
Vasiliy

回答:


6

記述したコードには、共有状態への3つのアクセスがあります。

var mutableVar = 0                        // access 1, init
withContext(Dispatchers.IO) {
    mutableVar = 1                        // access 2, write
}
System.out.println("value: $mutableVar")  // access 3, read

3つのアクセスは厳密に順番に並べられ、それらの間には同時性がありません。また、Kotlinのインフラストラクチャがスレッドプールに渡して呼び出し元のコルーチンに戻るときに、前に発生するエッジの確立を処理するので安心できIOます。

これは、おそらくより説得力があるように見える同等の例です。

launch(Dispatchers.Default) {
    var mutableVar = 0             // 1
    delay(1)
    mutableVar = 1                 // 2
    delay(1)
    println("value: $mutableVar")  // 3
}

以来はdelay懸濁関数であり、我々が使用しているので、Defaultスレッドプールによって支えられディスパッチャを、ライン1、2及び3は、それぞれ異なるスレッド上で実行することができます。したがって、発生前の保証に関する質問は、この例にも同様に当てはまります。一方、この場合、このコードの動作が順次実行の原則と一致していることは完全に明白です(私はそう思います)。


1
ありがとう。この質問をする動機となったのは、実際には「安心」の後の部分です。私が読むことができるドキュメントへのリンクはありますか?または、エッジが確立される前にこれが発生するソースコードへのリンクも非常に役立ちます(結合、同期、またはその他の方法)。
Vasiliy

1
これは非常に基本的な保証であるため、確認に時間を費やすこともありません。ボンネットの下でそれは沸騰しexecutorService.submit()、タスクの完了を待ついくつかの典型的なメカニズムがあります(CompletableFutureまたは類似のものを完了する)。Kotlinコルーチンの観点からは、ここでは同時実行性はまったくありません。
Marko Topolnik

1
あなたの質問は、「スレッドを一時停止してから別のコアで再開するときに、OSが前に発生することを保証しますか?」スレッドは、CPUコアとスレッドの関係をコルーチン化するためのものです。
Marko Topolnik、

1
ご説明ありがとうございます。しかし、なぜ私はそれが機能するのか理解するためにこの質問をしました。あなたの意見はわかりますが、今のところ、私が求めている厳密な答えではありません。
Vasiliy

2
ええと...私は実際、このスレッドがコードがシーケンシャルであることを確立したとは思わない きっと断言しています。私も、パフォーマンスに影響を与えることなく、サンプルが期待どおりに動作することを保証するメカニズムに興味があります。
G.ブレイクメイケ

3

Kotlinのコルーチンは、保証の前に発生します。

ルールは次のとおりです。コルーチンの内側に、コードの前には、関数呼び出しが一時停止する前に発生したコードの後に中断コール。

コルーチンは通常のスレッドのように考える必要があります。

Kotlinのコルーチンは複数のスレッドで実行できますが、変更可能な状態の観点からはスレッドと同じです。同じコルーチン内の2つのアクションを同時に実行することはできません。

ソース:https : //proandroiddev.com/what-is-concurrent-access-to-mutable-state-f386e5cb8292

コード例に戻ります。特にラムダがコルーチンである場合、ラムダ関数本体で変数をキャプチャすることは最良のアイデアではありません。ラムダの前のコードは、内部のコードの前には発生しません。

https://youtrack.jetbrains.com/issue/KT-15514を参照してください


実際のルールは次のとおりです。suspend関数呼び出しの前のコードは、suspend関数内のコードの前に発生し、suspend関数呼び出しの後のコードの発生します。これは、「コードのプログラム順序はコードの発生前の順序でもある」に一般化できます。そのステートメントには、中断可能な関数に固有のものが何もないことに注意してください。
Marko Topolnik
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.