You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何这两个Kotlin协程函数行为不同?一个正常一个发送阻塞

两个Kotlin协程函数运行差异的原因

先看给定的代码:

主函数

fun main() = runBlocking {
    val channel = Channel<Int>()
    foo(channel)
    for (y in channel) println(y)
    println("Done!")
}

正常运行的函数

fun CoroutineScope.foo(channel: Channel<Int>) {
    launch {
        for (x in 1..5) channel.send(x * x)
        channel.close()
    }
}

阻塞卡死的函数

suspend fun foo(channel: Channel<Int>) = coroutineScope {
    launch {
        for (x in 1..5) channel.send(x * x)
        channel.close()
    }
}

核心差异分析

首先要明确:Kotlin的Channel默认是RENDEZVOUS类型——没有缓冲区,send操作会挂起,必须等有接收方调用receive(或通过for循环迭代接收)才能继续执行。

第一个foo函数(正常运行)

  • 它是CoroutineScope的扩展函数,调用时直接在当前作用域(也就是runBlocking的协程作用域)下启动一个子协程。
  • 调用foo(channel)后,函数立即返回,不会阻塞runBlocking里的主协程。接下来主协程马上进入for (y in channel)开始接收数据,此时子协程的send操作能找到接收方,顺利发送所有数据,最后关闭Channel,for循环结束后打印Done!。

第二个foo函数(阻塞卡死)

  • 它是挂起函数,内部用coroutineScope创建了一个子作用域。coroutineScope的关键特性是:会挂起当前协程,直到内部所有子协程全部执行完毕才恢复。
  • 当主协程调用foo(channel)时,会被挂起,等待coroutineScope完成。但foo里的子协程执行channel.send时,主协程还没进入接收环节(因为foo还没返回),没有接收方,send会挂起这个子协程。
  • 这就形成了死锁:coroutineScope等子协程完成,子协程等接收方,而接收方(主协程的for循环)等foo返回才能执行,三方互相等待,程序彻底卡住。

总结

两个函数的本质区别在于协程作用域的生命周期管理:

  • 第一个foo在外部作用域启动独立子协程,不阻塞主流程,send和receive能同步配合。
  • 第二个foo用coroutineScope强制等待内部子协程完成,但子协程的send依赖主流程的接收,最终导致死锁。

内容的提问来源于stack exchange,提问作者igr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 11:55:39