Koltin协程比线程花费的时间更长

时间:2019-04-03 09:35:44

标签: kotlin concurrency coroutine kotlin-coroutines

我是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秒钟内完成了执行,这根本不是我所期望的。而且,如果我在列表中添加更多元素,执行时间也会增加。

那我在做什么错了?

2 个答案:

答案 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);
}
相关问题