我是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秒钟内完成了执行,这根本不是我所期望的。而且,如果我在列表中添加更多元素,执行时间也会增加。
那我在做什么错了?
答案 0 :(得分:2)
执行此代码所需的时间取决于用于计算的线程数。您的线程示例未定义绑定,并且产生的线程数与您拥有的作业数量相同。另一方面,协程示例将所有任务分派到内部使用Dispatchers.Default
线程池的GlobalScope
上。该池是有限的:
如果在上下文中未指定调度程序或任何其他ContinuationInterceptor,则所有标准构建器(如启动,异步等)都将使用默认的CoroutineDispatcher。
它由JVM上的共享线程池支持。默认情况下,此调度程序使用的最大线程数等于CPU内核数,但至少为两个。
假设您有4个核心。用4个作业运行代码将导致〜2s的运行时间,因为所有内容都是并行运行的(请注意并发<>并行性)。但是,一旦您有4个以上的任务,就必须等到第一个任务完成后才可以在任何时候同时运行4个任务。
您可以将调度程序池更改为具有更多线程的调度程序池:
GlobalScope.async(Dispatchers.IO)
请注意,delay
是长时间运行任务的不良示例。它不会阻塞调用者线程,因为它是一个真正的暂停功能,只能暂停协程。您实际上可以完全在main
上运行代码:
runBlocking {
val deferredResults =
(0..10).map { async(Dispatchers.IO) { longRunningJob() } }
deferredResults.awaitAll()
}
答案 1 :(得分:0)
runBlocking是协程函数。它的确切作用是“在主线程/调用线程中运行代码”。因此,它不是在创建并行线程来运行事物。
要异步运行代码,应使用启动功能而不是runBlocking。它在Dispatchers.Default个共享线程池上运行。
GlobalScope.launch {
delay(2000);
}