Kotlin协程joinAll与顺序join语义等价?实测矛盾求解
关于Kotlin协程joinAll与forEach { it.join() }语义等价的疑问
Kotlin协程官方文档提到:“此方法在语义上等价于通过forEach { it.join() }逐个加入所有给定的job”。
我编写了如下测试代码:
import kotlinx.coroutines.* fun main() { joinTest() joinAllTest() } fun joinAllTest() { val begin = System.currentTimeMillis() runBlocking { (0..2).map { launch { delay1s(it) } }.joinAll() } val end = System.currentTimeMillis() println("joinAllTest took: ${end-begin}") } fun joinTest() { val begin = System.currentTimeMillis() runBlocking { for (i in 0..2) { val job2 = launch { delay1s(it) } job2.join() } } val end = System.currentTimeMillis() println("joinTest took: ${end-begin}") } suspend fun delay1s(i: Int) { delay(1000) }
运行后输出结果为:
joinTest took: 3101 joinAllTest took: 1011
为什么文档会称二者语义等价?顺序调用join()会逐个挂起等待,而joinAll()是让任务并行运行,二者表现差异显著。
问题核心:Job的创建时机不同
文档里说的语义等价,是有前提的——所有Job已经被创建并启动。在这个前提下,joinAll()和forEach { it.join() }的最终效果完全一致:都是等待所有Job执行完毕,而且不会改变Job本身的执行模式(并行还是串行)。
你代码里的差异,根本不是join和joinAll的区别,而是Job启动的时机不同:
joinAllTest中,你先通过map { launch { ... } }一次性创建并启动了3个协程,它们一启动就并行跑起来了,之后调用joinAll()只是等着它们全部结束,所以总耗时约1秒。joinTest中,你是创建一个协程,立刻等着它做完,再创建下一个——相当于让3个协程串行执行,自然总耗时约3秒。
如果把joinTest改成先创建所有Job,再逐个调用join(),结果就和joinAllTest完全一致了:
fun joinTestFixed() { val begin = System.currentTimeMillis() runBlocking { // 先一次性创建并启动所有协程 val jobs = (0..2).map { launch { delay1s(it) } } // 再逐个等待它们完成 jobs.forEach { it.join() } } val end = System.currentTimeMillis() println("joinTestFixed took: ${end-begin}") }
运行这个修改后的函数,输出会和joinAllTest相近(约1秒),这才是文档所说的语义等价的场景。
总结
文档的语义等价,指的是针对已启动的Job集合,joinAll()和逐个调用join()的效果无差别——都会等待所有Job完成,且不影响Job原本的并行状态。你之前的代码差异是因为Job是串行创建启动的,和join、joinAll本身的行为无关。
内容的提问来源于stack exchange,提问作者fness
相关产品推荐
相关产品推荐

