Kotlin协程“在发生之前”的保证?

Vas*_*liy 6 concurrency kotlin happens-before kotlin-coroutines

Kotlin协程是否提供任何“事前保证”?

例如,mutableVar在这种情况下,对(可能)其他线程的写入和后续读取之间是否存在“先于后继”保证:

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

编辑:

也许其他示例可以更好地阐明这个问题,因为它更具Kotlin风格(可变性除外)。这段代码是线程安全的吗:

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

private data class Data(var data: Int)
Run Code Online (Sandbox Code Playgroud)

Mar*_*nik 6

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

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

严格按照顺序对这三个访问进行排序,它们之间没有并发性,并且您可以放心,Kotlin的基础结构会在切换到线程池并返回到调用协程时负责边缘发生事件IO

这是一个等效的示例,可能看起来更令人信服:

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

因为delay是可挂起的函数,并且由于我们使用的Default是由线程池支持的调度程序,所以第1、2和3行可能分别在不同的线程上执行。因此,您关于事前发生担保的问题同样适用于此示例。另一方面,在这种情况下(我希望)是完全显而易见的,该代码的行为与顺序执行的原理一致。

  • 谢谢。实际上是“放心”后面的部分促使我提出这个问题。有我可以阅读的文档链接吗?或者,链接到源代码(在建立边缘之前发生这种情况)也会有很大帮助(加入、同步或任何其他方法)。 (2认同)
  • 好吧……我实际上不认为该线程确定了代码是顺序的。它肯定地断言了它。我也很想看到一种机制,可以确保示例表现出预期的效果,而不会影响性能。 (2认同)

Ser*_*ich 4

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

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

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

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

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

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

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

(KT-15514 的声明)

lambda 之前的代码不会发生在内部代码之前。

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