android - 协程 - 带有连接的 Dispatchers.Main.immediate 在 runBlocking 内死锁
问题描述
分解一个在 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
在根中执行a ,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 提交问题。
解决方案
不回答原始问题,而是质疑问题本身;-)。
基于闩锁的解决方案“假装”为两个“作业”使用并发,但只有 io 作业可以。第二个 Runnable 只是包装要运行的代码,什么也不做。
这是做完全相同的事情,但更简单:
val latchMain = CountDownLatch(1)
val ioExecutor = Executors.newCachedThreadPool(Executors.defaultThreadFactory())
ioExecutor.execute(
Runnable {
Thread.sleep(1000)
latchMain.countDown()
}
)
latchMain.await()
基于“相同”协程的实现非常简单:
runBlocking {
val ioJob = launch(Dispatchers.IO) {
delay(1000)
}
ioJob.join()
}
或者对 io 作业使用相同的线程池:
runBlocking {
val ioJob = launch(ioExecutor.asCoroutineDispatcher()) {
delay(1000)
}
ioJob.join()
}
推荐阅读
- uber-api - 是否可以从 Uber API 中发现完成行程的车辆类型?
- c# - 没有控制台窗口的 C# 进程执行 Bath 文件(启动其他子进程)
- django-rest-framework - 如何获取由序列化程序中的视图创建和传递的查询集
- python - 尝试创建类时出现“AttributeError:'str' 对象没有属性'name'”错误
- oracle - Spring Data Rest 和 Oracle 在 2 个日期之间查询什么都不做
- r - 使用来自不同数据集的 geom_point 和 geom_smooth 添加图例
- ios - 如何让 iOS 尊重已安装的 Web 应用程序的清单文件?
- c# - 当 C# 从 C++ 调用函数时,如何编组包含结构作为输入/输出参数的映射?
- c# - 如何连接和断开代理?
- javascript - 如何使用预输入?