Kotlin协程比线程耗时更长

Kotlin协程比线程耗时更长,kotlin,concurrency,coroutine,kotlin-coroutines,Kotlin,Concurrency,Coroutine,Kotlin Coroutines,我是Kotlin和coroutines的新手,我正在努力理解CoroutinesAPI,所以很可能我做错了什么。所以我有一些对象的列表,我试图对每个对象应用一些长期运行的处理 val listOfFoos = listOf(Foo(1), ..., Foo(n)) listOfFoos.forEach { longRunningJob(it) } fun longRunningJob(foo: Foo) { runBlocking{ delay(2000) //hard

我是Kotlin和coroutines的新手,我正在努力理解CoroutinesAPI,所以很可能我做错了什么。所以我有一些对象的列表,我试图对每个对象应用一些长期运行的处理

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秒的时间,这似乎很好,因为每个
longlunningjob
都是并行运行的。 但是协同路由要好得多,因为它没有上下文切换线程那样的开销。下面是我使用协程的实现:

val deferredResults =
    listOfFoos.map { GlobalScope.async { longRunningJob(it) } }
runBlocking {
    deferredResults.awaitAll()
}
但是这个实现在大约4秒钟内完成执行,这根本不是我所期望的,如果我向列表中添加更多元素,执行时间也会增加


那么我做错了什么呢?

执行此代码所需的时间取决于用于计算的线程数。线程示例没有定义绑定,生成的线程数量与作业数量相同。另一方面,协同程序示例将所有任务分派到内部使用线程池的
GlobalScope
。此池是有限的:

如果在上下文中未指定dispatcher或任何其他ContinuationInterceptor,则由所有标准构建器(如launch、async等)使用的默认协同outineDispatcher

它由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()
}

runBlocking是一个协同程序函数。它所做的就是“在主线程/调用线程中运行代码”。所以它不是创建并行线程来运行东西

要异步运行代码,应该使用启动函数而不是runBlocking。它在共享的线程池上运行

GlobalScope.launch {
   delay(2000);
}

谢谢你详细的回答。关于
delay
,它被包装在
runBlocking
中,这不是阻塞吗?不,这不是“活动等待”,协同程序只是在给定的时间内暂停。替换它的最佳方法是什么?据我所知,
Thread.sleep
会阻塞整个线程,所有的协程都会被卡住,是吗?你说的“替换”是什么意思?对于协同程序,您不会使用
线程.睡眠
。但这对你的例子是有用的。它只会阻塞一个线程,就是您正在使用它的线程,我的意思是替换它,因此它将是一个长时间运行任务的好例子。它会阻止线程从而阻止在该线程上运行的所有协程,还是只阻止一个协程?