Kotlin 中的协程是轻量级线程,支持在不阻塞操作系统线程的情况下暂停和恢复计算。通过诸如 launch、async 等函数在父 CoroutineScope 存在时启动。
调度通过 CoroutineDispatcher 对象进行,确定协程在哪个线程/执行器中执行(Dispatchers.Main、Dispatchers.IO、Dispatchers.Default)。
示例:
fun main() = runBlocking { launch(Dispatchers.IO) { val data = getDataFromNetwork() withContext(Dispatchers.Main) { updateUI(data) } } }
取消: 协程通过取消其 job/Scope 来取消,这会抛出内部异常 CancellationException。协程应定期检查取消标志或调用暂停函数,以正确结束。
错误处理: 协程中的异常可能会“沉没”——例如,如果您未处理子协程中的错误,则它只会结束其工作,而父协程将不知道问题。为此,有 SupervisorJob 和 CoroutineExceptionHandler 机制。
上下文传递的细节:
如果在父协程内部,一个子协程因错误而结束,而其他子协程继续工作,会发生什么?所有子协程会被取消吗?
许多人错误地认为错误“会留在子协程中”.
正确答案: 如果使用普通的 Job(或 launch),那么任何子协程出错时,所有子协程都会自动取消。为了避免子协程被取消,可以使用 SupervisorJob 或 supervisorScope:
supervisorScope { launch { error("fail") } launch { println("这段代码将继续执行") } }
故事
错误的错误处理——流程崩溃:项目中使用协程加载数据。异常在子协程内“沉没”,导致重试/恢复级别无法正常工作,主屏幕保持空白,用户没有任何错误信息。
故事
由于在 Dispatchers.Main 上工作导致的 UI 死锁:一名年轻的 Android 开发者在 Dispatchers.Main 上启动了一个重计算任务——UI 开始“冻结”。没有意识到任何重计算应在 Dispatchers.Default 或 Dispatchers.IO 上执行。
故事
取消父作用域时的 AbortError:一位开发者未考虑到取消父作用域时,所有子协程都将因取消而结束,关键操作(数据保存)未能完成。