Котлин сопрограмм "случается раньше" гарантирует? - PullRequest
6 голосов
/ 23 октября 2019

Предоставляют ли сопрограммы 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)

Ответы [ 2 ]

6 голосов
/ 23 октября 2019

Код, который вы написали, имеет три доступа к общему состоянию:

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

Три доступа строго упорядочены последовательно, без параллелизма между ними, и вы можете быть уверены, что инфраструктура 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 могут выполняться в каждом потоке. Поэтому ваш вопрос о гарантиях произойдет до в равной степени относится к этому примеру. С другой стороны, в этом случае (я надеюсь) совершенно очевидно, что поведение этого кода соответствует принципам последовательного выполнения.

3 голосов
/ 23 октября 2019

Сопрограммы в Kotlin действительно обеспечивают, прежде чем гарантии.

Правило: внутри сопрограммы код до до вызова функции приостановки происходит до кода после вызова приостановки.

Вы должны думать о сопрограммах, как если бы они были обычными потоками:

Даже если сопрограмма в Kotlin может выполняться в нескольких потоках, онаэто как поток с точки зрения изменчивого состояния. Никакие два действия в одной и той же сопрограмме не могут быть одновременными.

Источник: https://proandroiddev.com/what-is-concurrent-access-to-mutable-state-f386e5cb8292

Возвращаясь к примеру кода. Захватывать переменные в теле лямбда-функций - не самая лучшая идея, особенно когда лямбда является сопрограммой. Код до лямбды не встречается до кода внутри.

См. https://youtrack.jetbrains.com/issue/KT-15514

...