Предоставляют ли сопрограммы 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)
withContext
, тогда как в первом примере он сначала создается, изменяется внутриwithContext
, а затем читает послеwithContext
. Таким образом, в первом примере реализованы дополнительные функции безопасности потоков.Ответы:
Код, который вы написали, имеет три доступа к общему состоянию:
Три доступа строго упорядочены по порядку, без параллелизма между ними, и вы можете быть уверены, что инфраструктура Kotlin позаботится об установлении границы «случай до того» при передаче в
IO
пул потоков и обратно в вызывающую сопрограмму.Вот эквивалентный пример, который может показаться более убедительным:
Поскольку
delay
это приостанавливаемая функция, и поскольку мы используемDefault
диспетчер, поддерживаемый пулом потоков, строки 1, 2 и 3 могут выполняться в разных потоках. Поэтому ваш вопрос о гарантиях « до и после» в равной степени относится и к этому примеру. С другой стороны, в этом случае (я надеюсь) совершенно очевидно, что поведение этого кода соответствует принципам последовательного выполнения.источник
executorService.submit()
что есть какой-то типичный механизм ожидания завершения задания (завершениеCompletableFuture
или что-то подобное). С точки зрения котлинских сопрограмм здесь вообще нет параллелизма.Сопрограммы в Котлине действительно предоставляются раньше, чем гарантии.
Правило таково: внутри сопрограммы код перед вызовом функции приостановки происходит перед кодом после вызова приостановки.
Вы должны думать о сопрограммах, как если бы они были обычными потоками:
Источник: https://proandroiddev.com/what-is-concurrent-access-to-mutable-state-f386e5cb8292
Возвращаясь к примеру кода. Захватывать переменные в органах с лямбда-функциями не самая лучшая идея, особенно когда лямбда является сопрограммой. Код до лямбды не встречается до кода внутри.
См. Https://youtrack.jetbrains.com/issue/KT-15514.
источник