从多个线程线程安全地访问 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 编译器将要求 interrupted
为 final
,而相同的 Java 代码将无法编译。
问题
- 仅使用标准库(即 w/o
java.util.concurrent.atomic.AtomicBoolean
或 kotlinx.atomicfu.AtomicBoolean
)重写上述代码的规范方法是什么?
- 如何以最可移植的方式重写上面的代码(使用协程的第二个片段),以便它可以针对 Kotlin/Multiplatform?
第一个解决方案是线程安全的数据结构,例如 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 个项目,您将无法使用它。
考虑以下 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 代码将无法编译。
问题
- 仅使用标准库(即 w/o
java.util.concurrent.atomic.AtomicBoolean
或kotlinx.atomicfu.AtomicBoolean
)重写上述代码的规范方法是什么? - 如何以最可移植的方式重写上面的代码(使用协程的第二个片段),以便它可以针对 Kotlin/Multiplatform?
第一个解决方案是线程安全的数据结构,例如 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 个项目,您将无法使用它。