Kotlin 协程 - 协程作用域 CoroutineScope
一、概念协程作用域 CoroutineScope 是一个接口没有任何抽象方法需要实现仅仅维护一个成员变量 CoroutineContext协程上下文将作为初始上下文对象传递给被创建的协程不同的实现类或作用域函数本质上的区别是持有的协程上下文不同配置不同。实现类(对象)提供环境核心库GlobalScope工厂函数MainScope( )CoroutineScope( )平台支持ViewModel.viewModelScopeLifecycleOwner.lifecycleScope扩展函数创建任务协程构建器launch( )async( )作用域函数业务代码普通函数runBlocking( )挂起函数coroutineScope( )supervisorScope( )withContext( )runInterruptible( )withTimeout( )withTimeoutOrNull( )二、普通函数 runBlocking()用于把阻塞式的普通函数改造成协程环境会阻塞当前线程直到其内部所有协程执行完毕。由于会阻塞线程在开发中不会使用一般用于改造main函数用来测试代码单元测试一般使用runTest。public actual fun T runBlocking(context: CoroutineContext, //不指定Dispatchers的话默认是BlockingEventLoop不是Defaultblock: suspend CoroutineScope.() - T): Tfun main(): Unit runBlocking { launch { println(...) } }三、实现类3.1 核心库3.1.1 GlobalScope单例对象不推荐使用。全局协程作用域不绑定到任何Job上无法取消通过它启动的子协程不会阻塞其所在线程可以一直运行到APP停止相当于守护线程不会阻止JVM结束运行子协程运行在自己的调度器上不会继承上下文与父协程没有联系因此所有开启的子协程都需要分别手动来管理容易造成内存泄漏和CPU冗余使用例如当Activity销毁后协程还在执行耗时操作占用资源。3.1.2 ContextScope上下文作用域intermal修饰未对外暴露根据指定的上下文创建协程作用域。使用工厂函数 MainScope()、CoroutineScope() 传入上下文对象参数获取到的就是 ContextScope 实例。3.2 工厂函数3.2.1 mainScope()默认上下文使用 SupervisorJob()Dispatchers.Main 的协程作用域。该调度器会绑定到主线程在Android中就是 UI Thread在 onDestroy() 中调用 scope.cancel() 关闭协程。可用于主动控制协程的生命周期对Android开发意义在于避免内存泄漏。public fun MainScope(): CoroutineScope ContextScope(SupervisorJob() Dispatchers.Main)class AndroidActivity{ private val scope MianScope() scope.launch{ launch{ coroutineContext //父协程的上下文 currentCoroutineContext() //当前协程的上下文 } } override fun onDestroy(){ scope.cancel() //手动在生命周期里释放资源 } }3.2.2 CoroutineScope()根据自定义上下文创建协程作用域如果上下文中没有 Job 会自动创建一个用于结构化并发。CoroutineScope是一个只包含 coroutineContext 属性的接口虽然我们可以创建一个实现类但这不是一个流行的做法而且存在不小心在其它地方取消作用域。通常我们会更喜欢通过对象来启动协程最简单的办法是使用 CoroutineScope() 工厂函数它用传入的上下文来创建作用域。public fun CoroutineScope(context: CoroutineContext): CoroutineScope ContextScope(if (context[Job] ! null) context else context Job())3.3 Android平台支持把所有耗时操作都定义为挂起函数一般情况下都是在 UI 或 ViewModel 中调用这两处都提供了协程作用域对象这样当生命周期结束的时候可以取消所有任务避免内存泄漏。当脱离这两个环境时就需要自定义作用域。3.3.1 ViewModel.viewModelScope()是ViewModelKTX 提供的扩展属性。使用的上下文是SupervisorJob() Dispatchers.Main.immediateViewModel销毁时协程作用域会自动被 cancel避免造成协程泄漏内存泄漏。public val ViewModel.viewModelScope: CoroutineScope3.3.2 LifecycleOwner.lifecycleScope()是 LifeCycleKTX 提供的扩展属性。使用的上下文是SupervisorJob() Dispatchers.Main.immediate在 Activity/Fragment 销毁时协程作用域会自动被 cancel避免造成协程泄漏内存泄漏。public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope3.3.3 自定义应用作用域相比于全局作用域 GlobalScope应用作用域 AppScope 可以在需要时取消所有协程可用于处理比单个屏幕声明周期更长的操作或独立于 UI 和 ViewModel 的单例工具类中如单例工具类中需要从DataStore中初始化状态而DataStore的读取是挂起函数。class App : Application() { private val exceptionHandler CoroutineExceptionHandler {context, throwable - //异常处理 } companion object { lateinit var appScope: CoroutineScope private set } override fun onCreate() { super.onCreate() appScope CoroutineScope(SupervisorJob() Dispatchers.Default exceptionHandler) } override fun onTerminate() { super.onTerminate() appScope.cancel() // 清理取消 } }四、扩展函数4.1 协程构建器 CoroutineBuilderlaunch() 和 async() 是 CoroutineScope 接口的扩展函数继承了它的 coutineContext 来自动传播其上下文元素和可取消性。挂起函数需要相互传递 Continuation每个挂起函数都要由另一个挂起函数或协程调用这一切都是从协程构建器创建协程开始的即作用域函数只能创建子协程协程构建器能创建根协程或子协程因为它通过实例调用可以存在于普通函数中。挂起函数创建的子协程是串行运行协程构建器创建的子协程是并行运行。参数 context指定协程上下文。默认为空的上下文。参数 start指定协程启动模式。默认为可以立刻被调度的状态。参数 block协程执行体即要做的任务。launch( )无需产生值public fun CoroutineScope.launch(context: CoroutineContext EmptyCoroutineContext,start: CoroutineStart CoroutineStart.DEFAULT,block: suspend CoroutineScope.() - Unit): Job返回一个Job实例用来管理协程的生命周期。async( )需要产生值public fun T CoroutineScope.async(context: CoroutineContext EmptyCoroutineContext,start: CoroutineStart CoroutineStart.DEFAULT,block: suspend CoroutineScope.() - T): DeferredT返回一个Deferred实例Job的子类通过 await() 拿到执行的结果包括异常。由于 await() 是挂起函数只能在协程作用域中调用因此不要用 async() 做根协程拿不到值就相当于 launch()。通常用于在协程作用域中构建并发子协程合并结果。4.1.1 协程启动模式 CoroutineStartCoroutineStart.DEFAULT协程创建后立即开始调度不一定此时就被线程执行了在被线程执行前如果协程被取消其将直接进入取消响应状态CoroutineStart.LAZY只要协程被需要时包括主动调用 start()、join()、await()才会开始调度。如果调度前被取消协程将进入异常结束状态。CoroutineStart.ATOMIC协程创建后立即开始调度内部代码执行到第一个挂起点之前不响应取消操作内部第一个挂起函数之前的代码一定执行。CoroutineStart.UNDISPATCHED协程被创建后立即在当前函数调用栈中执行所处的函数在哪个线程就是哪个即便该协程通过Dispatcher指定了运行的线程直到内部代码执行到第一个挂起点挂起函数运行完后之后的代码就是在Dispatcher指定的线程中运行了。//ATOMIC模式 val job launch(start CoroutineStart.ATOMIC) { //这里的代码一定会执行 delay(10000) //第一个挂起点 println(Job 完成) } delay(1000) job.cancel() //取消上面的job println(main 结束) //LAZY模式 val deferred async(start CoroutineStart.LAZY) { 27 } //此处执行一些计算...之后才需要拿到 deferred的值 deferred.await() //如果在这之前调用cancel()取消就直接抛异常JobCancellationException //UNDISPATCHED模式 launch(context Dispatchers.IO, start CoroutineStart.UNDISPATCHED) { println(挂起之前 Thread.currentThread().name) //打印main delay(10) println(挂起之后 Thread.currentThread().name) //打印DefaultDispatcher-worker-1 }五、作用域函数都是挂起函数不会阻塞线程。由于挂起需要协程环境只能由其它挂起函数或构建器调用因此只能用来创建子协程。形参上将Lambda的接收者指定为CoroutineScope因此可以在内部调用构建器开启子协程通常被用于包装函数一个作用域包装一堆调用尤其是withContext() 。异常结束自身supervisorScope()异常连锁反应指定上下文withContext()继承上下文coroutineScope()runInterruptible()限制执行时间withTimeout()withTimeoutOrNull()注意无法使用withContext(SupervisorJob())替代supervisorScope因为withContext仍会使用常规JobSupervisorJob只是它的父级Job。需要同时使用多个协程作用域函数的功能需要嵌套使用。suspend fun main() runBlocking { val scope CoroutineScope(Dispatchers.Default) scope.launch { delay(1000) println(协程1) } scope.launch { delay(1000) println(协程2) } // scope.cancel() //作用域被取消里面的子协程都会被取消 //delay是因为scope作用域自定义了上下文调度器没有继承父协程runBlicking的。 //它指定在了其它线程执行自己也没有挂起特性主线程会发现没有任务了会直接结束。 //也说明了协程不会阻塞线程。 delay(2000) }5.1 coroutineScope()异常会连锁取消子协程、兄弟协程和父协程。上下文继承上下文使用场景经常被用来包装一个挂起函数的主体。多用于并行分解任务逻辑。public suspend fun R coroutineScope(block: suspend CoroutineScope.() - R): Rsuspend fun main() runBlocking { //将main()函数内部改为支持协程式编写内部全部子协程运行完才会退出 //使用coroutineScope会挂起父协程runBlocking不然因为协程不会阻塞线程runBlocking会直接结束 coroutineScope { //这里换成supervisorScope子协程1就会执行 val job1 launch { delay(5000) println(子协程1) } val job2 launch { delay(1000) println(子协程2) throw Exception() //子协程1不会执行 } } }5.2 supervisorScope()异常不会影响兄弟协程和父协程。上下文使用 supervisorJob 而不是基于父协程构建自定义 Job 意味着失去结构化并发父协程的取消不会连带取消该子协程该子协程的异常不会向上传播。它是一个独立的作用域是根协程开启的子协程是顶级协程需要自己处理异常不会像 coroutineScope() 那样抛出。使用场景主要用于启动多个独立任务。public suspend fun R supervisorScope(block: suspend CoroutineScope.() - R): R5.3 withContext()异常会连锁取消子协程、兄弟协程和父协程。上下文指定上下文使用场景经常用来指定协程执行的线程和启动模式。这样在封装函数的时候里面的业务代码就会被执行在正确的线程中如 CPU密集型计算、IO、更新UIpublic suspend fun T withContext(context: CoroutineContext,block: suspend CoroutineScope.() - T): Tsuspend fun getSum(num1: Int, num2: Int): Int withContext(Despatcher.DEFAULT) { num1 num2 } suspend fun getData(): UserBean withContext(Despatcher.IO) { Retrofit.apiService.getData() }5.4 runInterruptible()异常会连锁取消子协程、兄弟协程和父协程。上下文指定上下文使用场景底层使用 withContext() 实现但会检查 Job 状态一旦协程取消会调用 Thread.interrupt() 来释放线程用来包装那些不支持挂起的 Java IO操作避免作用域中没有挂起点不会响应取消。public suspend fun T runInterruptible(context: CoroutineContext EmptyCoroutineContext,block: () - T): Tsuspend fun readFile() runInterruptible(Dispatchers.IO) { Thread.sleep(5000) File(data.txt).readText() }5.5 withTimeout()异常会连锁取消子协程、兄弟协程和父协程。上下文继承上下文使用场景超时未执行完会抛异常并返回一个值。超时抛出的TimeoutCancellationException是CancellationException子类因此不会影响其他协程。public suspend fun T withTimeout(timeout: Duration,block: suspend CoroutineScope.() - T): TwithTimeout(1000) { println() }5.6 withTimeoutOrNull()异常子协程异常会连锁取消其它子协程和自己。上下文继承上下文使用场景超时未执行完不抛异常返回null。用来包装那些出现异常后会一直等待的操作例如网络操作等待结果超过5s后不太可能会收到结果了。public suspend fun T withTimeoutOrNull(timeMillis: Long,block: suspend CoroutineScope.() - T): T?withTimeoutOrNull(1000) { println() } ?: 值为空六、创建协程的区别协程构建器和协程作用域函数中都包含了形参 block: suspend CoroutineScope.() - Unit。在这个带接收者的 lambda 里写代码可以通过属性 coroutineContext 拿到协程上下文能调用协程构建器去创建子协程也能调用挂起函数去挂起当前协程。全局GlobalScope阻塞runBlocking( )生命感知MainScope( )ViewModel.viewModelScopeLifecycleOwner.lifecycleScope自定义CoroutineScope( )挂起异常结束自身supervisorScope( )异常连锁反应指定上下文withContext( )继承上下文coroutineScope( )限制执行时间withTimeout( )withTimeoutOrNull( )通过协程构建器创建通过协程作用域函数创建①通过协程作用域实例调用创建的可能是根协程在普通函数中也可能是子协程在其它协程中②直接调用创建的只能是子协程必须存在父协程即外部有上面说的block包裹。创建的只能是子协程。挂起函数“挂起恢复”的特性只能在协程环境下实现因此只能在其它挂起函数或协程中调用一定存在父协程。携带来自CoroutineScope的协程上下文异常通过Job传递给父协程构建器是并发执行的但可调用 join()、await()携带Continuation的协程上下文异常像普通函数那样抛出挂起函数是阻塞的按顺序执行tihs是被创建出来的协程因为调用block的是被创建出来的协程协程都是 AbstractCoroutine 的子类而它实现了CoroutineScope并不是调用它的协程作用域对象。因此在block中创建子协程①通过协程构建器能直接通过 coroutineScope 属性拿到当前协程的上下文继承从而形成父子关系具备传播取消和异常。②通过作用域函数父协程被挂起时会返回一个 Continuation 续体对象给该挂起函数这个续体就是父协程协程都是 AbstractCoroutine 的子类而它实现了Continuation从而获取并继承上下文形成父子关系。③通过协程作用域对象调用协程构建器不是子协程因为没有上下文继承关系无法通过Job传播取消和异常。GlobalScope是EmptyCoroutineContext而MainScop、ViewModelScope、LifecyleScope都是指定的SupervisorJob()至于CoroutineScope()虽然构造可以传入父协程上下文构建内外相同的作用域何必多此一举。GlobalScope.launch { //不是子协程外层的取消不会取消它 CoroutineScope(Dispatchers.IO).launch { } //子协程虽然创建了新的作用域对象但是通过coroutineContext获取了父协程上下文间接继承 CoroutineScope(coroutineContext).launch { } //不是子协程Job会替换掉父协程中的Job CoroutineScope(coroutineContext Job()).launch { } }多任务-串行launch 多个 withContext多任务-并行launch 多个 asynclaunch 多个 launch七、多个协程作用域之间的关系在处理过程中需要执行一个额外的非必要操作例如收集数据分析若是存在于挂起函数中则会额外等待这个操作执行完若这个操作发生异常还会波及正常的业务这个时候最好是放在一个单独的作用域里启动它。一般通过构造单元测试、控制此作用域或者函数注入如果是为了调用一些函数使用SupervisorJob如果是收集异常信息使用CoroutineExceptionHandler。在结构化并发中由于作用域存在嵌套使用因此有多种情况类型场景举例异常传播特征顶级作用域1.根协程之间。2.GlobalScope嵌套GlobalScope彼此独立互不影响。3.A和B是两个作用域对象A开启的作用域中B开启了作用域两个作用域彼此独立互不影响。4.supervisorScope() 或 supervisorJob 由于使用了新的 Job相当于是一个独立的根协程与外部互不影响。不向外传播。协同作用域外层有父协程且自身非另外的作用域对象开启。双向传播。主从作用域外层有父协程自身是supervisorScope()或supervisorJob。与内部直接子协程主从与外部协同。向下单向传播。

相关新闻

终极go2rtc流媒体服务器:从零开始的完整安装配置指南

终极go2rtc流媒体服务器:从零开始的完整安装配置指南

终极go2rtc流媒体服务器:从零开始的完整安装配置指南 【免费下载链接】go2rtc Ultimate camera streaming application 项目地址: https://gitcode.com/GitHub_Trending/go/go2rtc go2rtc是一款功能强大的开源流媒体服务器,专为摄像头和视频流处理…

2026/7/31 17:43:01 阅读更多 →
Scrcpy-iOS终极指南:如何在iPhone上远程控制Android设备

Scrcpy-iOS终极指南:如何在iPhone上远程控制Android设备

Scrcpy-iOS终极指南:如何在iPhone上远程控制Android设备 【免费下载链接】scrcpy-ios Scrcpy-iOS.app is a remote control tool for Android Phones based on [https://github.com/Genymobile/scrcpy]. 项目地址: https://gitcode.com/gh_mirrors/sc/scrcpy-ios …

2026/7/31 17:43:01 阅读更多 →
MySQL让自增的ID重新变得连续

MySQL让自增的ID重新变得连续

MySQL让自增的ID重新变得连续 MYSQL在创建一个带有自增主键ID的表时,通常在删除一条中间某一条或者多条数据时,导致中间自增主键不连续了。使用下面的SQL脚本可以重置主键。 作为自己的笔记,方便以后自己查找 -- 1. 初始化用户变量 SET new_i…

2026/7/31 17:43:01 阅读更多 →

最新新闻

布列韦肽Hepcludex作用机制:靶向NTCP受体,从源头阻断HBV/HDV肝细胞入侵路径

布列韦肽Hepcludex作用机制:靶向NTCP受体,从源头阻断HBV/HDV肝细胞入侵路径

传统乙肝抗病毒药物以抑制病毒逆转录、核酸合成为主要作用靶点,仅能控制乙肝病毒复制,无法作用于依附乙肝存活的丁肝病毒,这也是既往丁肝治疗收效甚微的核心原因,布列韦肽 Hepcludex 采用全新的病毒入胞抑制机制,作用靶…

2026/7/31 18:15:13 阅读更多 →
用好AiPy Pro(爱派),大幅提升工作效率——我的数据分析工具制作深度使用经验分享

用好AiPy Pro(爱派),大幅提升工作效率——我的数据分析工具制作深度使用经验分享

官方名称:AiPy Pro|中文名:爱派|开发商:知道创宇 一、它到底是什么? AiPy Pro是一款融合大语言模型与Python能力的AI智能体平台,主打本地部署、代码生成、任务自动化。简单来说,它…

2026/7/31 18:15:13 阅读更多 →
准备开两融前的避坑指南!现在融资融券利率最低是多少?

准备开两融前的避坑指南!现在融资融券利率最低是多少?

大家好,我是ArthurGoo~最近私信里问两融的朋友特别多,我看大家在准备跨入这个门槛之前,脑子里全是一连串的问号:资产和交易年限到底怎么算?加了杠杆亏钱了是不是必须补现金?平仓线到了会怎样&am…

2026/7/31 18:15:13 阅读更多 →
内网穿透的应用-服务器日志出现异常怎么办?Python监控、钉钉告警与远程处理教程

内网穿透的应用-服务器日志出现异常怎么办?Python监控、钉钉告警与远程处理教程

前言 服务器运行过程中,磁盘空间不足、程序连接失败或者服务异常,通常都会在系统日志或应用日志中留下记录。但如果没有专门的监控工具,这些信息往往要等到人工登录服务器检查时才会被发现。 对于家庭服务器、测试主机和数量不多的小型服务…

2026/7/31 18:15:13 阅读更多 →
Linux Optimizer常见问题解答:解决安装与使用中的10大难题

Linux Optimizer常见问题解答:解决安装与使用中的10大难题

Linux Optimizer常见问题解答:解决安装与使用中的10大难题 【免费下载链接】Linux-Optimizer Linux Optimizer 项目地址: https://gitcode.com/gh_mirrors/li/Linux-Optimizer Linux Optimizer是一款专为Linux服务器设计的自动化优化工具,支持Ubu…

2026/7/31 18:15:13 阅读更多 →
多角色对话系统部署实战:从环境配置到性能优化

多角色对话系统部署实战:从环境配置到性能优化

这次我们来看一个名为《舌战群儒》的项目。从名称来看,这很可能是一个基于语言模型或对话系统的技术实现,可能涉及多轮对话、辩论场景模拟或多人对话生成能力。这类项目通常关注语言模型的逻辑推理、上下文理解和多角色交互能力。对于这类语言对话项目&a…

2026/7/31 18:14:13 阅读更多 →

日新闻

物理复制比逻辑复制好在哪?数据库复制原理详解

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:00:34 阅读更多 →
BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:00:34 阅读更多 →
有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:00:34 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/31 1:03:03 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/31 4:19:39 阅读更多 →

月新闻