如何确定我是否在Android / Kotlin中线程不足?
我正在构建一个需要从远程API加载大量数据的应用程序。我在代码中添加日志以检查线程名,并且看到至少有5个Workers并行运行。该应用程序具有“刷卡刷新”功能,如果我刷卡过多,经过一定数量的通话后,我会以某种方式丢失数据(尽管我没有从服务器收到错误响应)。我观察到,我感兴趣的呼叫从一个工作人员开始,然后该工作人员被另一个进程占用。然后,该方法将永远无法完成。我有点不解。请提供有关如何解决多线程问题的任何建议的帮助。将Dispatcher.IO
更改为Dispatcher.Default
的行为没有太大区别。
我可以将所有网络呼叫一个接一个地(按顺序进行)-这样,即使我刷了100次刷新,我也不会丢失任何数据。但是,所有调用都在同一工作线程上进行,因此我没有利用并行性。 :-/
答案 0 :(得分:1)
TL; DR:使用协程时是否可能用完线程?答案是否定的(死锁是另一个问题)。但是,是否可能以某种方式使用协程,这意味着您的并发性受线程数量的约束?是的。
我认为您必须首先了解的是阻止功能和非阻止/暂停/异步功能之间的区别。
一个真正的挂起/非阻塞/异步功能,具有一些长时间运行的功能,但是在完成该长时间运行的任务之前正确地控制了执行,这是您真正利用协程获得的并发性的方式。让我演示一下。
在一个线程上具有内部长期运行挂起功能的多个协程
val singleThread = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
fun main() = runBlocking {
val start = System.currentTimeMillis()
val jobs = List(10) {
launch (singleThread){
delay(1000)
print(".")
}
}
jobs.forEach { it.join() }
val end = System.currentTimeMillis()
println()
println(end-start)
}
在这里,我们有10个协程已通过1个线程快速启动。它们都使用挂起函数delay
来模拟耗时1000毫秒的长时间运行的任务。但是...整个过程在1018毫秒内完成。对于熟悉基于纯线程的并发的人来说,这有点奇怪。解释来了。但是,为了明确起见,这是相同的代码,只是使用Thread.sleep
而不是delay
。
1个线程上的多个协程具有内部长期运行阻止功能
fun main() = runBlocking {
val start = System.currentTimeMillis()
val jobs = List(10) {
launch (singleThread){
Thread.sleep(1000)
print(".")
}
}
jobs.forEach { it.join() }
val end = System.currentTimeMillis()
println()
println(end-start)
}
这部分相同的代码,但阻塞Thread.sleep
花费了10027毫秒。每个协程都会阻塞它所在的线程,因此,我们的10个协程实际上是串行执行的。执行长时间运行的功能时,未将控制权交还给调度程序。
您可以阅读更详细的解释,以了解无阻塞挂起与Roman Elizarov here
的阻塞呼叫之间的区别就您而言,我怀疑您正在使用阻塞的IO库进行数据检索。这意味着这些调用中的每一个都会阻塞它所在的线程,并且在IO任务完成时不会将控制权交给调度程序。
我的建议是:
但是同时执行操作会丢失数据吗?
这里没有足够的信息来确定,但是,我认为您没有以解释并发的方式构建逻辑。在真正的并行执行中,滑动次数3可能会在滑动2或滑动1完成之前完成。如果更新不是幂等的,或者每个更新请求都提供了部分数据集,那么您可能先处理其他更新3,而在最终到达时忽略更新1和2。