Kotlin协程“happens-before”保证?

5fjcxozz  于 2023-06-24  发布在  Kotlin
关注(0)|答案(2)|浏览(133)

Kotlin协程是否提供任何“之前发生”的保证?
例如,在这种情况下,在写入mutableVar和随后在(可能)其他线程上读取之间是否存在“happens-before”保证:

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

atmip9wb1#

你写的代码有三种对共享状态的访问:

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

这三个访问是严格按顺序排序的,它们之间没有并发性,你可以放心,Kotlin的基础设施在切换到IO线程池并返回到调用协程时会负责建立一个 happens-before 边缘。
下面是一个可能看起来更有说服力的等效示例:

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

由于delay是一个可挂起的函数,并且由于我们使用的是由线程池支持的Default分派器,因此第1、2和3行可以分别在不同的线程上执行。因此,你关于 happen-before 保证的问题同样适用于这个例子。另一方面,在这种情况下,(我希望)这段代码的行为与顺序执行的原则是一致的,这是完全显而易见的。
KotlinCoroutines的主要作者Roman Elizarov在blog post中提出了同样的观点。相关报价:
尽管Kotlin中的协程可以在多个线程上执行,但从可变状态的Angular 来看,它就像一个线程。同一协程中没有两个动作可以并发。

oxf4rvwz

oxf4rvwz2#

Kotlin中的协程确实提供了保证之前的发生。
规则是:在协程中,suspend函数调用之前的代码发生在suspend调用之后的代码**之前。
你应该把协程看作是常规线程:
尽管Kotlin中的协程可以在多个线程上执行,但从可变状态的Angular 来看,它就像一个线程。同一协程中没有两个动作可以并发。
来源: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

相关问题