ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

kotlinx-coroutines-core 模块完全指南:协程核心原语、构建器与调度器深度解析

kotlinx-coroutines-core 模块完全指南:协程核心原语、构建器与调度器深度解析 kotlinx-coroutines-core 模块完全指南协程核心原语、构建器与调度器深度解析【免费下载链接】kotlinx.coroutinesLibrary support for Kotlin coroutines项目地址: https://gitcode.com/gh_mirrors/ko/kotlinx.coroutines导读本文以 kotlinx.coroutines 仓库的核心模块 kotlinx-coroutines-core 为骨架系统梳理协程构建器launch / async / produce / runBlocking、调度器Dispatchers、同步原语Mutex / Semaphore / Channel、Flow 冷热流、select 表达式等核心 API并结合仓库源码如 Builders.common.kt、Dispatchers.common.kt、Channel.kt、Mutex.kt 等逐一说明其类型签名、使用方式与底层实现要点。读完本文你将能够依据一份 API 速查表 快速定位并正确选用 kotlinx-coroutines-core 中的每一种核心原语理解冷流与热流的本质区别并掌握结构化并发、取消与超时的正确姿势。kotlinx-coroutines-core是整个 kotlinx.coroutines 生态的基石模块提供了与协程交互所需的最底层、最通用的原语协程构建器、协程调度器、上下文元素、同步原语、Flow 数据流与select 表达式。仓库中其他模块如kotlinx-coroutines-test、kotlinx-coroutines-debug、reactive 系列、ui 系列都在此基础上构建。在仓库目录中该模块的源码按平台组织为多套 source setcommon/src公共源码、jvm/src、js/src、native/src、jsAndWasmShared/src、wasmJs/src、wasmWasi/src、web/src以及concurrent/src实验性并发原语。绝大多数 API 在common/src中以expect声明、在各平台中以actual实现因此同一套 API 在 JVM、JS、Native含 Darwin 与 wasm 目标上行为一致。模块的公开二进制接口API 签名被固化在 kotlinx-coroutines-core.api 与 kotlinx-coroutines-core.klib.api 中这也是模块稳定性承诺的一部分。一、协程构建器四种启动协程的方式kotlinx-coroutines-core提供四个顶层协程构建器它们都以CoroutineScope作为接收者是日常开发中使用频率最高的入口名称返回类型作用域说明launchJobCoroutineScope启动一个不返回结果的协程asyncDeferredCoroutineScope返回一个携带未来结果的DeferredproduceReceiveChannelProducerScope生产元素流runBlockingTCoroutineScope阻塞当前线程直至协程运行结束1.1 launch异步无返回值任务launch在 Builders.common.kt 中定义其 KDoc 明确指出它启动当前CoroutineScope的一个子协程child coroutine不阻塞当前线程返回该协程的引用Jobblock是并发执行的新协程计算体协程在其block以及 block 中创建的所有子协程完成之前都视为活动active状态context参数用于指定附加的上下文元素与CoroutineScope.coroutineContext合并在context中传入Job是被禁止的写法仅出于向后兼容不抛异常因为它会破坏结构化并发默认情况下协程被调度到其ContinuationInterceptor上执行不保证立即开始运行甚至可能在启动前被取消此时其代码不会执行start参数CoroutineStart可用于调整该行为若block抛出CancellationException协程被视为已取消若抛出其他异常则被视为失败failed。launch创建的新协程上下文按如下规则构造先将CoroutineScope的上下文与context参数经newCoroutineContext合并大多数情况下context中的元素覆盖 scope 中的元素然后把CoroutineScope.coroutineContext中的Job作为新协程的父 Job并把新协程自己的Job加入上下文。最终得到的上下文即block接收者CoroutineScope的coroutineContext。关于取消与等待Job.cancel()取消协程Job.join()挂起直至其完成不阻塞线程且join在协程被取消或失败时也正常返回cancelAndJoin()是先取消再等待的便捷组合。若以CoroutineStart.LAZY启动协程不会立即被调度需要显式Job.start()或通过Job.join()触发执行。由于launch不返回结果协程若以异常失败调用方一般无法可靠获知该异常此时应改用async。1.2 async获取异步结果async与launch的差异在于返回值它返回 Deferred——一个继承了Job、并携带未来结果的接口。通过Deferred.await()可以挂起并取回结果若协程以异常失败await会重新抛出该异常。async是需要在另一个协程中访问结果时的推荐选择。1.3 produce协程版生产者produce启动一个以 ProducerScope 为接收者的协程返回 ReceiveChannel。它把协程与通道结合在produce块内调用send即可向返回的通道推送元素当通道被关闭/取消或协程完成时生产者协程会自我取消。其实现可参考 Produce.kt。1.4 runBlocking桥接阻塞世界runBlocking会阻塞当前线程直到其内部协程完成因此它是唯一一个会阻塞线程的构建器用于在main函数、测试或需要从非协程代码如 Java 回调进入协程世界时使用。在协程内部不要调用runBlocking否则会造成线程阻塞与死锁风险。1.5 结构化并发要点从源码的 KDoc 可以提炼出结构化并发的四条核心规则以launch为例父CoroutineScope的生命周期不会在自身所有子协程完成前结束父作用域被取消子协程也会被取消子协程以非CancellationException异常失败、且父作用域上下文持有非 supervisor 的Job时父Job会以该异常被取消若父作用域持有SupervisorJob或根本没有Job如GlobalScope或畸形作用域异常无法向上传播将按 CoroutineExceptionHandler 的规则处理。若在context中显式传入Job新协程的生命周期将不再与CoroutineScope绑定作用域取消不影响该协程协程失败也不取消作用域而作用域可能在协程完成前就结束——新代码切勿依赖此行为。二、协程调度器Dispatchers 家族调度器是协程的ContinuationInterceptor决定协程在哪个线程/线程池上执行。kotlinx-coroutines-core在 Dispatchers.common.kt 中以expect object Dispatchers声明了三个核心调度器各平台提供actual实现如 JVM 上的 Dispatchers.kt名称说明Dispatchers.Main将协程执行限定到 UI 线程Dispatchers.Default将协程执行限定到一个共享的后台线程池Dispatchers.Unconfined完全不限定协程执行的线程CoroutineDispatcher.limitedParallelism创建给定调度器的一个视图限制并行执行的任务数2.1 Dispatchers.Default默认共享线程池Dispatchers.Default是标准构建器launch、async等在上下文未指定任何调度器或ContinuationInterceptor时使用的默认调度器。在 JVM 与 Native 上它由共享线程池支撑默认最大线程数等于 CPU 核心数但至少为 2。适合执行 CPU 密集型任务。2.2 Dispatchers.MainUI 主线程Dispatchers.Main将协程限定到与 UI 对象交互的主线程通常单线程。访问该属性在 classpath 中没有主调度器时会抛出IllegalStateException。它在不同平台映射到不同实现源码注释明确说明JVM通过ServiceLoader选择可能是 Android 主线程调度器、JavaFx 或 Swing EDT 调度器需要把对应构件加入运行时依赖——kotlinx-coroutines-androidAndroid 主线程、kotlinx-coroutines-javafxJavaFx Application 线程、kotlinx-coroutines-swingSwing EDT这三个构件位于仓库 ui 目录下JS等价于带immediate支持的Default调度器Native Darwin由 Darwin 主队列main queue支撑其他 Native 目标不可用测试场景下可用kotlinx-coroutines-test的Dispatchers.setMain将主调度器替换为 mock。2.3 Dispatchers.Unconfined不限定线程Dispatchers.Unconfined不把协程限定到任何特定线程它让协程的初始延续在当前调用帧call-frame中执行此后由相应挂起函数所使用的线程恢复执行不强制任何线程策略。嵌套在该调度器中启动的协程会形成一个事件循环以避免栈溢出。源码文档给出了一个关键提醒withContext(Dispatchers.Unconfined) { println(1) launch(Dispatchers.Unconfined) { // 嵌套的 unconfined 协程 println(2) } println(3) } println(Done)输出既可能是1 2 3也可能是1 3 2事件循环顺序属于实现细节、可能变化但可以保证的是Done只在withContext与launch的代码全部完成之后才会打印。若希望协程恢复后被限定到某个线程/线程池、但首个挂起点前仍在当前调用帧执行可改用构建器的CoroutineStart.UNDISPATCHED参数。2.4 limitedParallelism限制并行度CoroutineDispatcher.limitedParallelism返回原调度器的一个视图限制同时执行的任务数量是控制并发上限的通用手段例如限制对共享资源的并发访问。其实现位于 CoroutineDispatcher.ktJVM 上还有针对Dispatchers.Default与 IO 的优化实现可参考 Dispatchers.kt。三、更多上下文元素与取消支持3.1 NonCancellableNonCancellable 是一个永远处于活动状态、不可取消的 Job。将其放入上下文即可在取消区域中执行不可被取消的收尾代码withContext(NonCancellable) { // 即使外层协程已被取消这里的代码也会执行完成 }典型用途是在协程被取消后仍需执行的清理/资源释放逻辑。此外用户自定义的挂起函数可以通过 suspendCancellableCoroutine 获得可取消挂起的支持其核心机制是 CancellableContinuationImpl 中维护的取消处理器与prompt cancellation guarantee见下文withTimeout一节。3.2 CoroutineExceptionHandlerCoroutineExceptionHandler 是捕获未处理异常的上下文元素。当异常既没有被try/catch捕获、又没有可向上传播的父 Job如SupervisorJob或GlobalScope场景时异常会交给CoroutineExceptionHandler处理。四、同步原语Mutex 与 Semaphorekotlinx.coroutines.sync包提供面向协程的同步原语源码位于 sync 目录名称挂起函数说明Mutexlock互斥Semaphoreacquire限制最大并发数4.1 Mutex协程互斥锁Mutex 的 KDoc 明确了其关键语义只有locked / unlocked两种状态并且不可重入即使从当前持有锁的同一个线程/协程再次调用lock依然会挂起等待JVM 上内存语义与synchronized块类似一次unlock操作 happens-before 其后的每一次对该 Mutex 的lock失败的tryLock不产生内存效应lock是可取消挂起函数等待期间 Job 被取消会立即以CancellationException恢复且存在prompt cancellation guarantee即使函数已准备好返回结果、但在挂起期间被取消也会抛出CancellationException若CancellationException抛出前该函数已获得锁会先释放锁lock是公平的挂起的调用者按先进先出FIFO顺序恢复官方建议优先使用withLockKDoc 多次强调 It is recommended to use withLock for safety reasons确保临界区结束时一定释放锁、且永远不会在未成功获取锁时调用unlocktryLock(owner)非挂起地尝试获取锁失败返回falseowner是调试用的所有者令牌若 mutex 已被同一令牌同一身份持有会抛出IllegalStateException。典型用法val mutex Mutex() suspend fun updateSharedResource() { mutex.withLock { // 临界区更新共享状态 } }4.2 Semaphore并发上限Semaphore 用于限制最大并发数acquire()挂起直至获得许可release()归还许可。它是限制对某资源并发访问量的通用原语与Mutex只允许一个持有者的语义互补。五、Channel协程间通信Channel 是kotlinx.coroutines.channels包的核心本质是协程间传递元素流的非阻塞队列/交换器queue or exchanger。发送方接口 SendChannel 与接收方接口 ReceiveChannel 组合成完整的Channel。核心方法对接口挂起版本非挂起版本SendChannelsendtrySendReceiveChannelreceive / receiveCatchingtryReceive5.1 send 的挂起语义与取消保证send的 KDoc 详细说明了挂起与取消行为若通道的溢出策略为BufferOverflow.SUSPENDsend可能挂起具体取决于通道容量rendezvous容量 0发送者挂起直到接收方调用receiveunlimited / conflated即使BufferOverflow.SUSPEND也永远不会挂起buffered默认容量或自定义容量发送者挂起直到缓冲区有空位send是可取消的等待期间 Job 被取消会立即以CancellationException恢复并且存在prompt cancellation guarantee——即使元素已成功发送、但在挂起期间被取消也会抛出CancellationException。这意味着抛异常不一定代表元素未送达未送达元素undelivered elements的处理方式参见Channel文档相关章节未挂起时send不检查取消在紧循环中应使用ensureActive或isActive定期检查通道已关闭时send会抛异常建议使用trySend(element).onClosed { ... }模式来稳健地处理通道已关闭的情形源码中的isClosedForSend检查示例明确标注了 DANGER! THIS CHECK IS NOT RELIABLE!因为检查与发送之间存在并发关闭的窗口。5.2 通道容量常量Channel工厂定义了容量常量定义在 Channel.kt 的Factory中RENDEZVOUS默认容量 0、UNLIMITED、CONFLATED、BUFFERED默认容量CHANNEL_DEFAULT_CAPACITY即 64等。各容量对应的行为差异是选择通道类型的首要依据。5.3 produce 与通道的结合produce构建器见第一节就是协程 Channel的典型结合生产者协程内部向ProducerScope发送元素外部通过返回的ReceiveChannel消费通道关闭或协程取消时生产者自动清理。六、Flow冷流与热流kotlinx.coroutines.flow包提供构建异步值流的 API。模块文档用一张表区分cold冷流与hot热流两类流的构造方式名称类型说明flowcold运行一个生成器风格的代码块来发射值flowOfcold发射作为参数传入的值channelFlowcold运行给定代码向其中通道发送即等价于从 flow 发射callbackFlowcold将基于回调的 API 转换为 flowReceiveChannel.consumeAsFlowhot将通道转换为 flow把收到的所有值发射给单个订阅者ReceiveChannel.receiveAsFlowhot将通道转换为 flow把收到的值分发给多个订阅者MutableSharedFlowhot允许把每个值同时发射给任意多个订阅者MutableStateFlowhot将可变状态表示为 flow冷流与热流的本质区别模块 README 的原始定义冷流coldstream是某种产生值的过程该过程为每个订阅者分别执行热流hotstream无论是否存在订阅者都使用同一个值源。6.1 flow 构建器与上下文纪律flow 创建一个coldflow每次对结果应用终结操作符terminal operator如collect时block都会重新执行。其 KDoc 给出的斐波那契示例展示了无限流的懒计算特性fun fibonacci(): FlowBigInteger flow { var x BigInteger.ZERO var y BigInteger.ONE while (true) { emit(x) x y.also { y x } } } fibonacci().take(100).collect { println(it) }flow的关键实现细节源码可查flow返回SafeFlow继承AbstractFlow收集时调用collectSafely执行block默认可取消每次emit都会调用ensureActive检查取消上下文纪律emit必须严格在block的调度器中执行以保持 flow 上下文否则抛出IllegalStateException。例如withContext(Dispatcher.IO) { emit(2) }会失败——需要切换上下文时应使用flowOn操作符一系列asFlow()扩展可以把函数、挂起函数、Iterable、Iterator、Sequence等一键转换为 flow相关实现都在 Builders.kt。6.2 channelFlow 与 callbackFlowchannelFlow与callbackFlow都提供向通道发送即从 flow 发射的能力适用于需要并发生产元素或对接回调式 API 的场景它们与flow的主要差异在于内部使用通道缓冲、允许在多个协程中并发发射。callbackFlow专用于把回调式 API如网络监听器封装成 flow并在回调注册/注销时正确处理生命周期。6.3 热流SharedFlow 与 StateFlowMutableSharedFlow每个发射的值可以同时被任意多个订阅者接收没有单订阅者限制MutableStateFlow表示可变状态的 flow始终保存最新值新订阅者立刻收到当前值。二者是 Android / 响应式 UI 场景中最常使用的热流原语源码与测试如 SharedFlow.kt、StateFlow.kt 以及 common 测试目录下的大量用例可用于深入学习其背压、重放与状态更新语义。七、select 表达式同时等待多个挂起操作select 表达式同时等待多个挂起函数的结果直到其中一个成功。模块文档给出了完整的挂起函数 → select 子句 → 非挂起版本对照表接收者挂起函数select 子句非挂起版本JobjoinonJoinisCompletedDeferredawaitonAwaitisCompletedSendChannelsendonSendtrySendReceiveChannelreceiveonReceivetryReceiveReceiveChannelreceiveCatchingonReceiveCatchingtryReceive无delayonTimeout无select的典型价值是竞速例如同时等待多个数据源或通道取先到者onTimeout子句则为 select 提供超时兜底。select 相关实现位于 selects 目录测试用例在 selects 目录。八、顶层挂起函数速查kotlinx.coroutines包提供的顶层挂起函数定义分散在common/src各文件中如 Delay.kt、Yield.kt、Builders.common.kt、Timeout.kt、Await.kt名称说明delay非阻塞睡眠yield在单线程调度器中让出线程withContext切换到不同的上下文withTimeout设置执行时限超时抛异常withTimeoutOrNull设置执行时限超时返回 nullawaitAll等待所有给定任务成功完成或任一任务异常完成joinAll等待所有给定任务结束8.1 withTimeout 与取消的协作性withTimeout 的源码揭示了两个易踩的坑非正超时立即失败timeMillis 0时立即抛出TimeoutCancellationExceptionblock根本不会执行取消是协作式的withTimeout不会自动停止 block 内的代码运行——它只是取消正在运行的 blockblock 需要自行感知取消例如通过挂起或检查isActive。KDoc 明确举例一段不检查取消的 JVM 代码即使超时也会完整运行如耗时 10 秒的纯 CPU 计算。此外超时触发的取消与 block 的执行是并发的可能在 block 完成之后、调用方拿到结果之前发生。withTimeoutOrNull与withTimeout的差异仅在超时行为前者超时返回null而不是抛异常适合超时可接受的场景。九、模块内部的包结构地图模块 README 后半部分按包package给出了一级导航各包的源码位置与职责如下包名职责源码位置commonkotlinx.coroutines通用协程构建器、上下文与辅助函数common/srckotlinx.coroutines.sync同步原语mutex 与 semaphoresynckotlinx.coroutines.channels通道——协程间传递元素流的非阻塞原语channelskotlinx.coroutines.flowFlow——异步冷/热元素流flowkotlinx.coroutines.selectsSelect——同时执行多个挂起操作直至其一成功selectskotlinx.coroutines.intrinsics对协程进行更细粒度控制的底层原语intrinsicskotlinx.coroutines.futureJDK 8CompletableFuture支持JVMfuture/Future.ktkotlinx.coroutines.streamJDK 8Stream支持JVMstreamkotlinx.coroutines.time基于 JDK 8Duration的现有时间操作符重载JVMtime其中future、stream、time三个包仅存在于 JVM 平台对应源码位于 jvm/src 之下intrinsics包为 intrinsics 目录下的底层实现如startCoroutineCancellable、suspendCoroutineUninterceptedOrReturn的封装一般应用开发者很少直接使用。十、深入阅读与验证路径若想进一步验证本文所述的实现细节建议直接查阅仓库中的以下路径构建器与结构化并发Builders.common.ktlaunch / async / runBlocking 的完整 KDoc 与契约、Job.kt、JobSupport.ktJob 状态机New → Active → Completing → Cancelling → Cancelled/Completed、CoroutineScope.kt调度器Dispatchers.common.ktexpect 声明与平台映射说明、JVM 实现 Dispatchers.kt、CoroutineDispatcher.kt取消与超时CancellableContinuation.kt、CancellableContinuationImpl.kt、Timeout.kt、NonCancellable.kt同步与通道Mutex.kt、Semaphore.kt、Channel.kt、Produce.ktFlowBuilders.kt、Channels.kt、SharedFlow.kt、StateFlow.kt测试佐证各原语的测试用例位于 common/testchannels / flow / selects / sync 子目录与 jvm/test含 scheduling、exceptions、guide 等例如Mutex相关测试、Channel容量行为测试等可作为理解语义的活文档API 契约kotlinx-coroutines-core.apiJVM ABI与 kotlinx-coroutines-core.klib.apiKotlin 多平台库 ABI。说明本文中涉及的 API 名称、默认值如Dispatchers.Default线程数、BUFFERED容量与行为均以当前仓库源码为准Kotlin 官方 API 文档kotlinlang.org作为参考链接仅用于辅助理解不在本文引用范围内展开。【免费下载链接】kotlinx.coroutinesLibrary support for Kotlin coroutines项目地址: https://gitcode.com/gh_mirrors/ko/kotlinx.coroutines创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表