Kotlin 協程多線程調度器和局部變量的線程安全

瓦爾

讓我們考慮這個帶有協程的簡單代碼

import kotlinx.coroutines.*
import java.util.concurrent.Executors

fun main() {
    runBlocking {
        launch (Executors.newFixedThreadPool(10).asCoroutineDispatcher()) {
            var x = 0
            val threads = mutableSetOf<Thread>()
            for (i in 0 until 100000) {
                x++
                threads.add(Thread.currentThread())
                yield()
            }
            println("Result: $x")
            println("Threads: $threads")
        }
    }
}

據我所知,這是相當合法的協程代碼,它實際上產生了預期的結果:

Result: 100000
Threads: [Thread[pool-1-thread-1,5,main], Thread[pool-1-thread-2,5,main], Thread[pool-1-thread-3,5,main], Thread[pool-1-thread-4,5,main], Thread[pool-1-thread-5,5,main], Thread[pool-1-thread-6,5,main], Thread[pool-1-thread-7,5,main], Thread[pool-1-thread-8,5,main], Thread[pool-1-thread-9,5,main], Thread[pool-1-thread-10,5,main]]

問題是什麼使局部變量的這些修改成為線程安全的(或者是線程安全的?)。我知道這個循環實際上是按順序執行的,但它可以在每次迭代時更改正在運行的線程。在第一次迭代中從線程完成的更改仍然應該對在第二次迭代中拾取此循環的線程可見。哪個代碼可以保證這種可見性?我試圖將此代碼反編譯為 Java 並使用調試器挖掘協程實現,但沒有找到線索。

馬爾科·托波爾尼克

您的問題完全類似於操作系統可以在線程執行過程中的任何時候掛起線程並將其重新調度到另一個 CPU 內核的認識。這不是因為所討論的代碼是“多核安全的”,而是因為它保證了單個線程根據其程序順序語義進行行為的環境。

Kotlin 的協程執行環境同樣保證了順序代碼的安全性。您應該根據此保證進行編程,而不必擔心它是如何維護的。

如果您出於好奇而想深入了解“如何”的細節,那麼答案將變成“視情況而定”。每個協程調度器都可以選擇自己的機制來實現它。

作為一個有啟發性的例子,我們可以專注於您在發布的代碼中使用的特定調度程序:JDK 的fixedThreadPoolExecutor. 你可以向這個執行器提交任意任務,它會在單個(任意)線程上執行每個任務,但是一起提交的許多任務將在不同的線程上並行執行。

此外,執行服務提供了保證代碼導致到executor.execute(task) 之前發生在該任務中的代碼,並在該任務中的代碼之前發生另一個線程的觀察其完成(future.get()future.isCompleted(),從關聯得到一個事件CompletionService)。

Kotlin 的協程調度器通過依賴執行器服務的這些原語來驅動協程通過其暫停和恢復的生命週期,從而獲得整個協程的“順序執行”保證。每當協程掛起時,提交給執行器的單個任務就會結束,當協程準備好恢復時(當用戶代碼調用continuation.resume(result)時,調度程序會提交一個新任務

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章