从多个线程线程安全地访问 Kotlin var

Thread-safe access to a Kotlin var from multiple threads

考虑以下 Kotlin 代码:

import kotlin.concurrent.thread

fun main() {
    println("Press <Enter> to terminate.")

    var interrupted = false

    val worker = thread {
        while (!interrupted) {
            println("Working...")
            Thread.sleep(1000L)
        }
    }

    System.`in`.read()

    println("Terminating...")
    interrupted = true

    worker.join()

    println("Terminated.")
}

以及使用协程重写的相同示例:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    println("Press <Enter> to terminate.")

    var interrupted = false

    val worker = launch(Dispatchers.IO) {
        while (!interrupted) {
            println("Working...")
            delay(1000L)
        }
    }

    System.`in`.read()

    println("Terminating...")
    interrupted = true

    worker.join()

    println("Terminated.")
}

这两个示例在大多数情况下都可以工作,但都被破坏了,因为在字节码级别,从多个线程访问的 boolean 变量表示为 kotlin.jvm.internal.Ref.BooleanRef不是线程安全的。

值得一提的是,Java 编译器将要求 interruptedfinal,而相同的 Java 代码将无法编译。

问题

  1. 仅使用标准库(即 w/o java.util.concurrent.atomic.AtomicBooleankotlinx.atomicfu.AtomicBoolean)重写上述代码的规范方法是什么?
  2. 如何以最可移植的方式重写上面的代码(使用协程的第二个片段),以便它可以针对 Kotlin/Multiplatform?

基于Kotlin documentation

第一个解决方案是线程安全的数据结构,例如 AtmoicBoolean

import java.util.concurrent.atomic.AtomicBoolean
import kotlin.concurrent.thread
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() {
    println("Press <Enter> to terminate.")
    val interrupted = AtomicBoolean()
    val worker = thread {
        while (!interrupted.get()) {
            println("Working...")
            Thread.sleep(1000L)
        }
    }

    System.`in`.read()
    println("Terminating...")
    interrupted.set(true)
    worker.join()
    println("Terminated.")
}

// coroutine way
fun main_2() = runBlocking {
    println("Press <Enter> to terminate.")
    val interrupted = AtomicBoolean()
    val worker = launch(Dispatchers.IO) {
        while (!interrupted.get()) {
            println("Working...")
            delay(1000L)
        }
    }

    System.`in`.read()
    println("Terminating...")
    interrupted.set(true)
    worker.join()
    println("Terminated.")
}

第二种解决方案是Mutual exclusion

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

val mutex = Mutex()

fun main() = runBlocking {
    println("Press <Enter> to terminate.")
    var interrupted = false
    val worker = launch(Dispatchers.IO) {
        while (mutex.withLock { !interrupted }) {
            println("Working...")
            delay(1000L)
        }
    }

    System.`in`.read()
    println("Terminating...")
    mutex.withLock { interrupted = true }
    worker.join()
    println("Terminated.")
}

我在这里使用了两种解决方案来解决这个问题,您可以在 here 中找到其他解决方案

如何以最可移植的方式重写上面的代码(使用协程的第二个片段),以便它可以针对 Kotlin/Multiplatform?
我在 kotlin-multiplatform 方面没有太多经验,但是你不能在 kotlin multiplatform 中使用 Dispacher.IO 因为它绑定到 JVM,所以如果你 使用 Kotlin/JavaScript 或 Kotlin/Native 个项目,您将无法使用它。