14

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)
4

2 回答 2

8

您编写的代码对共享状态具有三种访问权限:

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 行可能各自在不同的线程上执行。因此,您关于发生前保证的问题同样适用于此示例。另一方面,在这种情况下(我希望)很明显,这段代码的行为与顺序执行的原则是一致的。

于 2019-10-23T10:28:23.520 回答
4

Kotlin 中的协程确实提供了先发生的保证。

规则是:在协程中,挂起函数调用之前的代码发生在挂起调用之后的代码之前。

您应该将协程视为常规线程:

尽管 Kotlin 中的协程可以在多个线程上执行,但从可变状态的角度来看,它就像一个线程。同一个协程中的两个动作不能同时发生。

来源:https ://proandroiddev.com/what-is-concurrent-access-to-mutable-state-f386e5cb8292

回到代码示例。在 lambda 函数体中捕获 var 并不理想,尤其是当 lambda 是协程时。实际上,

将可变变量 (var) 捕获到此类块的范围内几乎总是一个错误

(来自 KT-15514 的声明)

lambda 之前的代码不会发生在里面的代码之前。

请参阅https://youtrack.jetbrains.com/issue/KT-15514

于 2019-10-23T09:07:01.630 回答