编程Kotlin 软件工程师

Kotlin 中的协程如何工作?描述启动机制、调度、取消和错误处理。举例说明使用情况,并解释与上下文和异常传递相关的细节。

用 Hintsage AI 助手通过面试

答复

Kotlin 中的协程是轻量级线程,支持在不阻塞操作系统线程的情况下暂停和恢复计算。通过诸如 launchasync 等函数在父 CoroutineScope 存在时启动。

调度通过 CoroutineDispatcher 对象进行,确定协程在哪个线程/执行器中执行(Dispatchers.MainDispatchers.IODispatchers.Default)。

示例:

fun main() = runBlocking { launch(Dispatchers.IO) { val data = getDataFromNetwork() withContext(Dispatchers.Main) { updateUI(data) } } }

取消: 协程通过取消其 job/Scope 来取消,这会抛出内部异常 CancellationException。协程应定期检查取消标志或调用暂停函数,以正确结束。

错误处理: 协程中的异常可能会“沉没”——例如,如果您未处理子协程中的错误,则它只会结束其工作,而父协程将不知道问题。为此,有 SupervisorJob 和 CoroutineExceptionHandler 机制。

上下文传递的细节:

  • 上下文(例如,Job、Dispatcher、自定义元素)在创建新协程时会继承。
  • 如果作用域被关闭或取消,则不能简单地在作用域之间传递参数。

具有挑战性的问题

如果在父协程内部,一个子协程因错误而结束,而其他子协程继续工作,会发生什么?所有子协程会被取消吗?

许多人错误地认为错误“会留在子协程中”.

正确答案: 如果使用普通的 Job(或 launch),那么任何子协程出错时,所有子协程都会自动取消。为了避免子协程被取消,可以使用 SupervisorJob 或 supervisorScope:

supervisorScope { launch { error("fail") } launch { println("这段代码将继续执行") } }

由于对主题细节的无知而导致的真实错误示例


故事

错误的错误处理——流程崩溃:项目中使用协程加载数据。异常在子协程内“沉没”,导致重试/恢复级别无法正常工作,主屏幕保持空白,用户没有任何错误信息。


故事

由于在 Dispatchers.Main 上工作导致的 UI 死锁:一名年轻的 Android 开发者在 Dispatchers.Main 上启动了一个重计算任务——UI 开始“冻结”。没有意识到任何重计算应在 Dispatchers.Default 或 Dispatchers.IO 上执行。


故事

取消父作用域时的 AbortError:一位开发者未考虑到取消父作用域时,所有子协程都将因取消而结束,关键操作(数据保存)未能完成。