挂起函数如何不阻塞主线程?

7
在 Dispatchers.Main 上启动的协程在挂起时不会阻塞主线程。这是什么意思?因此,当在主线程上启动挂起函数并有一些较长的行时,它是否会自动分配到新线程中?这很令人困惑。

1
一个被挂起的协程不会占用任何线程。就像一个当前未被调度运行的线程不会占用任何CPU核心一样。 - Marko Topolnik
2个回答

7

一个挂起的函数会在一个线程上运行,就像普通函数一样,但是当它切换到另一个线程时,不会阻塞主线程。

普通函数在从主线程切换到后台线程时,必须阻塞主线程,因为它不知道执行后从哪里继续。

    ...
    val result = getResult()  // This blocks the main thread due to join
    use(result);

fun getResult(): String {
    var result: String = ""
    val t = Thread(Runnable {
        ...
        result = "woohoo"
    })
    t.start()
    t.join()  // Main thread is waiting for the result to return it
    return result
}

但当一个挂起函数切换线程时,它不会阻塞主(前一个)线程,因为它知道在哪里继续执行。
launch(Dispatchers.Main) {
    ...
    val result = getResult()  // This will not block the main thread
    use(result)
}

suspend fun getResult(): String = withContext(Dispatchers.IO) {
    ...
    "woohoo"
}

1

Dispatchers.Main 是一个 CoroutineContext,它将协程调度到主线程中,但当协程本身暂停时,即通过更改上下文或线程或其他某些原因,则“主线程变为空闲状态”,底层的 Continuation 对象负责在此之后继续执行。

由于挂起时没有任务在主线程上运行,因此它是空闲的,并且能够通过上下文(Dispatcher)接受另一个任务,因此被记录为非阻塞状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接