【问题标题】:Kotlin Coroutine launch{} vs launch{ withContext{} }Kotlin 协程启动{} 与启动{ withContext{} }
【发布时间】:2021-01-29 13:16:31
【问题描述】:

我的 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 仍然显示警告。 The post说这只是Android Studio的一个bug,这个解决方案很好。

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

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

【问题讨论】:

    标签: android multithreading kotlin kotlin-coroutines


    【解决方案1】:

    是的,这是一个带有警告的错误。 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 有。而且有些 Dispatcher 有很多线程。

    【讨论】:

      【解决方案2】:

      Android Studio 中似乎确实存在错误。以下代码对我没有任何警告:

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

      您还应该知道,在此代码和您共享的两个代码之间,行为几乎没有区别。在这三种情况下,代码都将在 IO 线程上执行。

      • CoroutineScope(context)
      • launch(context)
      • withContext(context

      所有这些方法只是指定协程上下文。默认情况下,launch 使用协程范围上下文,但您可以像我上面所做的那样或使用 withContext 来更改它。

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

      Dispatchers.IO 实际上默认为 64 个线程。

      【讨论】:

        猜你喜欢
        • 2023-03-22
        • 2020-07-15
        • 2019-06-26
        • 1970-01-01
        • 2020-10-15
        • 2019-03-20
        • 2020-04-12
        • 2017-12-11
        • 2020-09-15
        相关资源
        最近更新 更多