分解一个在Android上挂起主线程并与协程进行并发处理的简单案例,下面的代码只打印Launched
和 runBlocking
永远不会完成:
runBlocking {
val ioJob = launch(Dispatchers.IO) {
delay(1000)
}
val mainJob = launch(Dispatchers.Main.immediate) {
Log.d("Routine", "Launched")
ioJob.join()
Log.d("Routine", "Joined")
}
listOf(mainJob, ioJob).joinAll()
}
当然,如果我们替换 Dispatchers.Main.immediate
与 Dispatchers.IO
一切都很好,但是我的一些并发处理应该在 main 上运行。使用 Dispatchers.Main
正如预期的那样,不记录任何内容。似乎曾经 join
在 runBlocking
的根目录中执行它使发送到 main 的任何挂起的东西都瘫痪。值得注意的是,基于
CountDownLatch
的方法和线程工作得很好: val latchMain = CountDownLatch(1)
val primaryLatch = CountDownLatch(2)
val ioExecutor = Executors.newCachedThreadPool(Executors.defaultThreadFactory())
log("Execute IO")
ioExecutor.execute(
Runnable {
log("Delay IO")
Thread.sleep(1000)
log("Countdown Main")
latchMain.countDown()
Thread.sleep(3000)
primaryLatch.countDown()
})
log("Execute Main")
Runnable {
log("Await Main")
latchMain.await()
log("Countdown Primary")
primaryLatch.countDown()
}.run()
log("Await Primary")
primaryLatch.await()
log("Continue")
stepTracker.endTracking()
return stepTracker.stepGraphTrace
}
private fun log(msg: String) = Log.i("Routine", "[${Thread.currentThread().name}] $msg")
带输出:2021-08-11 11:04:06.508 [main] Execute IO
2021-08-11 11:04:06.509 [main] Execute Main
2021-08-11 11:04:06.510 [main] Await Main
2021-08-11 11:04:06.510 [pool-25-thread-1] Delay IO
2021-08-11 11:04:07.512 [pool-25-thread-1] Countdown Main
2021-08-11 11:04:07.513 [main] Countdown Primary
2021-08-11 11:04:07.513 [main] Await Primary
2021-08-11 11:04:10.514 [main] Continue
有任何想法吗?即将就这个问题向 JetBrains 提交问题。
最佳答案
假设 runBlocking
代码是从主线程调用的,那么在 Dispatchers.Main 上启动的内部协程将永远不会到达主线程 Looper 上的队列前面,因为 runBlocking
仍然阻塞主线程,所以协程永远无法启动。
当您使用 Main.immediate
,启动的协程的开头至少可以运行因为immediate
如果您已经在主线程上,则将协程运行到第一个暂停点,如果您启动了 runBlocking
在主线程上。当它到达挂起函数调用join()
,它被放入主线程looper的队列中,你又回到了与上面相同的问题。挂起函数调用总是打断协程的延续。
关于android - 协程 - 带有连接的 Dispatchers.Main.immediate 在 runBlocking 内死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68734377/