首页 > 解决方案 > Kotlin 协程启动{} vs 启动{ withContext{} }

问题描述

我的 Android 应用程序需要在后台(在服务内)进行一些文件读/写,首先我使用:

CoroutineScope(Dispatchers.IO).launch {
    val fos = openFileOutput(fileName, MODE_PRIVATE)
    val oos = ObjectOutputStream(fos)
    oos.writeObject(myObj)
    oos.close()
}

块内的每一行都有一个警告:“不适当的阻塞方法调用”

搜索问题后,我想我理解了 80%。所以基本上大多数协程只有 1 个线程,如果它被阻塞,那个协程将没有线程来做其他工作。为了解决这个问题,我们应该像这样把它包裹起来withContext

CoroutineScope(Dispatchers.IO).launch {
    withContext(Dispatchers.IO) {
        val fos = openFileOutput(fileName, MODE_PRIVATE)
        val oos = ObjectOutputStream(fos)
        oos.writeObject(myObj)
        oos.close()
    }
}

Android Studio 仍然显示警告。帖子说这只是Android Studio中的一个错误,这个解决方案很好。

我不明白的是,withContext仍在运行Dispatchers.IO。从launch块来看,它可能看起来像是非阻塞的,但如果Dispatchers.IO只有 1 个线程并且withContext块在该线程上运行,那么该线程仍然被阻塞,不是吗?

我还了解到Dispatchers.IO实际上有几乎无限的线程,它只是在需要时创建一个新线程。所以withContext实际上不是阻塞,但如果这是真的,为什么我们需要withContext阻塞?如果可以在需要时创建线程,那么第一个代码不会有任何问题Dispatchers.IO,因此永远不会被阻塞,对吧?

标签: androidmultithreadingkotlinkotlin-coroutines

解决方案


是的,这是一个带有警告的错误。Lint 无法检测到作用域正在使用什么 Dispatcher,我想他们只是假设您正在使用上下文使用的作用域 Dispatchers.Main,因为这是最常见的。

您的 CoroutineScope (伪)构造函数具有带有 的上下文Dispatchers.IO,因此launch如果它不修改它,则继承该上下文,因此您启动的协程也使用Dispatchers.IO. 所以,你的withContext块是多余的。

解决方法是在启动时指定调度程序:

CoroutineScope(Job()).launch(Dispatchers.IO) {
    val fos = openFileOutput(fileName, MODE_PRIVATE)
    val oos = ObjectOutputStream(fos)
    oos.writeObject(myObj)
    oos.close()
}

另外,您的声明:

所以基本上大多数协程只有 1 个线程,如果它被阻塞,那个协程将没有线程来做其他工作。

具有误导性。协程没有线程,而 Dispatcher 有。有些调度程序有很多线程。


推荐阅读