从多个线程对 Kotlin var 进行线程安全访问



考虑以下 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 编译器需要interrupted才能final,并且相同的 Java 代码根本无法编译。

问题

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

基于 Kotlin 文档

第一种解决方案是线程安全的数据结构,如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.")
}

第二种解决方案是互斥

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.")
}

我只是使用两种解决方案来解决这个问题,在这里您可以找到另一种解决方案

如何以最可移植的方式重写上面的代码(使用协程的第二个片段(,以便它可以针对 Kotlin/Multiplatform?


我在 kotlin 多平台方面没有太多经验,但你不能在 Kotlin 多平台中使用"Dispacher.IO",因为它绑定到 JVM,所以如果你是 使用 Kotlin/JavaScript 或 Kotlin/Native 项目,您将无法使用它。

最新更新