为什么 withContext 在完成时不恢复到原始调度程序?

Why isn't withContext resuming to the original dispatcher on completion?

我正在编写一个应该 运行 在特定调度程序上的挂起函数。它可以从任何调度程序调用,因此它应该在返回时恢复到原始调度程序。

withContext 应该有所帮助,因为它指出:

This function uses dispatcher from the new context, shifting execution of the block into the different thread if a new dispatcher is specified, and back to the original dispatcher when it completes.

这是一个基本测试:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.setMain
import kotlinx.coroutines.withContext

fun log(message: String = "") {
    println("[${Thread.currentThread().name}] $message")
}

suspend fun test9Odd(x: Int): Boolean {
    return withContext(Dispatchers.IO) {
        log("test9Odd for x = $x...")
        delay(250)
        x and 1 == 1
    }
}

fun main(args: Array<String>) {
    // setting Dispatcher.Main for the test:
    val x = TestCoroutineDispatcher()
    x.runCurrent()
    Dispatchers.setMain(x)

    runBlocking {
        withContext(Dispatchers.Main) {
            log("test9")
            for (x in listOf(8, 97, 108)) {
                val result = test9Odd(x)
                log("Is $x odd? ${if (result) "yes" else "no"}")
            }
        }
    }
}

我观察到的与上面的说法矛盾,上下文切换但返回时不恢复,它保持运行ning on worker-1:

    [main] test9
    [DefaultDispatcher-worker-1] test9Odd for x = 8...
    [DefaultDispatcher-worker-1] Is 8 odd? no
    [DefaultDispatcher-worker-1] test9Odd for x = 97...
    [DefaultDispatcher-worker-1] Is 97 odd? yes
    [DefaultDispatcher-worker-1] test9Odd for x = 108...
    [DefaultDispatcher-worker-1] Is 108 odd? no

我的问题是:为什么 main() 函数 运行ning 中的代码在第一次调用后没有回到主线程?

请注意,如果我在主函数中删除 withContext 然后 它会切换回主线程。但是,如果我用 launch 替换 withContext,它不会改变这个问题。

我知道协程测试直到最近才被描述为“不稳定”并且难以以产生预期结果的方式进行设置。

他们最近对测试库进行了大修,不推荐使用 TestCoroutineDispatcher。

以下测试对我有效:

class Tests {

    @Before
    fun before() {
        Dispatchers.setMain(StandardTestDispatcher())
    }

    @Test
    fun `switch between dispatchers`() = runTest {
        withContext(Dispatchers.Main) {
            log("test9")
            for (x in listOf(8, 97, 108)) {
                val result = test9Odd(x)
                log("Is $x odd? ${if (result) "yes" else "no"}")
            }
        }
    }

}

输出:

[Test worker @coroutine#1] test9
[DefaultDispatcher-worker-1] test9Odd for x = 8...
[Test worker @coroutine#1] Is 8 odd? no
[DefaultDispatcher-worker-1] test9Odd for x = 97...
[Test worker @coroutine#1] Is 97 odd? yes
[DefaultDispatcher-worker-1] test9Odd for x = 108...
[Test worker @coroutine#1] Is 108 odd? no

这也有效:

    fun before() {
        Dispatchers.setMain(newSingleThreadContext("main"))
    }

运行 它在这样的测试之外也有效:

fun main() {
    Dispatchers.setMain(newSingleThreadContext("main"))
    runBlocking {
        withContext(Dispatchers.Main) {
            log("test9")
            for (x in listOf(8, 97, 108)) {
                val result = test9Odd(x)
                log("Is $x odd? ${if (result) "yes" else "no"}")
            }
        }
    }
}