kotlin - Kotlin 协程比线程花费更长的时间
问题描述
我是 Kotlin 和协程的新手,我正在尝试理解协程 API,所以很可能我做错了什么。所以我有一些对象的列表,我正在尝试对这些对象中的每一个应用一些长时间运行的处理。
val listOfFoos = listOf(Foo(1), ..., Foo(n))
listOfFoos.forEach { longRunningJob(it) }
fun longRunningJob(foo: Foo) {
runBlocking{
delay(2000) //hardcoded delay for testing
}
//do something else
}
当然,这是并发运行它的完美候选,所以这里它使用了良好的旧线程:
listOfFoos.map { thread(start = true) { longRunningJob(it) } }.forEach { it.join() }
当我使用它测量它的执行时间时,measureTimeMillis
它给了我大约 2 秒,这似乎非常好,因为每个都longRunningJob
并行运行。但是协程要好得多,因为它没有上下文切换的线程这样的开销。所以这是我使用协程的实现:
val deferredResults =
listOfFoos.map { GlobalScope.async { longRunningJob(it) } }
runBlocking {
deferredResults.awaitAll()
}
但是这个实现在大约 4 秒内完成了执行,这根本不是我所期望的,如果我向列表中添加更多元素,执行时间也会增加。
那么我在这里做错了什么?
解决方案
执行此代码所需的时间取决于用于计算的线程数。您的线程示例未定义绑定并产生与您的作业一样多的线程。另一方面,协程示例将GlobalScope
内部使用Dispatchers.Default
线程池的所有任务分派到。这个池是有限的:
如果在上下文中未指定调度程序或任何其他 ContinuationInterceptor,则所有标准构建器(如启动、异步等)使用的默认 CoroutineDispatcher。
它由 JVM 上的共享线程池支持。默认情况下,此调度程序使用的最大线程数等于 CPU 内核数,但至少为两个。
假设您有 4 个核心。使用 4 个作业运行代码将导致大约 2 秒的运行时间,因为一切都是并行运行的(注意并发 <> 并行性)。但是,一旦您有超过 4 个任务,就必须等到第一个任务之一完成,因为在任何时候只有 4 个任务可以同时运行。
您可以将调度程序池更改为具有更多线程的调度程序池:
GlobalScope.async(Dispatchers.IO)
请注意,这delay
是一个长时间运行的任务的坏例子。它不会阻塞调用者线程,因为它是一个真正的挂起函数,只会暂停协程。您实际上可以main
完全运行您的代码:
runBlocking {
val deferredResults =
(0..10).map { async(Dispatchers.IO) { longRunningJob() } }
deferredResults.awaitAll()
}
推荐阅读
- reactjs - 在 React.js 中单击外部时如何隐藏图层?
- python - 如何在 DynamoDB 中查询 begin_with 的主键?
- hardware - 如何从辅助端口读取原始位?
- python - 在创建新的 Conda 虚拟环境后,Tensorflow 的训练速度非常慢
- laravel - 如何创建自定义页面并将其显示在 Laravel Nova 的菜单中?
- mpandroidchart - MPAndroidChart groupBars()“每组间隔”计算公式
- google-cloud-platform - GCP AI Platform Unified - AutoScaling
- java - Persistence.createEntityManagerFactory 错误
- google-apps-script - 谷歌表格增加选定的单元格
- flutter - 如何获取 TextButton 以获取其父 AlertDialog 的宽度?