Jef*_*hen 9 multithreading android kotlin kotlin-coroutines
我的 Android 应用程序需要在后台(在服务内)执行一些文件读/写操作,首先我使用:
CoroutineScope(Dispatchers.IO).launch {
val fos = openFileOutput(fileName, MODE_PRIVATE)
val oos = ObjectOutputStream(fos)
oos.writeObject(myObj)
oos.close()
}
Run Code Online (Sandbox Code Playgroud)
块内的每一行都有一个警告:“不适当的阻塞方法调用”
搜索完问题后,我想我明白了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()
}
}
Run Code Online (Sandbox Code Playgroud)
Android Studio 仍然显示警告。帖子说这只是Android Studio中的一个错误,这个解决方案很好。
我不明白的是,withContext仍然在运行Dispatchers.IO。从launch块来看,它可能看起来像非阻塞,但如果Dispatchers.IO只有 1 个线程并且withContext块在该线程上运行,那么该线程仍然被阻塞,不是吗?
我还了解到Dispatchers.IO实际上有无限的线程,它只是在需要时创建一个新线程。所以withContext实际上并没有阻塞,但如果这是真的,为什么我们需要阻塞呢withContext?如果可以在需要时创建线程,因此第一个代码不会有任何问题,Dispatchers.IO因此永远不会被阻塞,对吗?
是的,这是一个带有警告的错误。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()
}
Run Code Online (Sandbox Code Playgroud)
另外,你的声明:
所以基本上大多数协程只有 1 个线程,如果它被阻塞,那么该协程将没有线程去做其他工作。
具有误导性。协程没有线程,调度程序有。有些调度程序有很多线程。
看来Android Studio确实存在一个bug。以下代码对我没有显示任何警告:
CoroutineScope(Dispatchers.IO).launch(Dispatchers.IO) {
val fos = context.openFileOutput("", Context.MODE_PRIVATE)
val oos = ObjectOutputStream(fos)
oos.writeObject(myObj)
oos.close()
}
Run Code Online (Sandbox Code Playgroud)
您还应该知道,此代码与您共享的两个代码之间的行为实际上没有区别。在所有三种情况下,代码都将在 IO 线程上执行。
CoroutineScope(context)launch(context)withContext(context所有这些方法都只是指定协程上下文。默认情况下launch使用协程范围上下文,但您可以像我上面所做的那样或使用withContext.
所以基本上大多数协程只有 1 个线程,如果它被阻塞,那么该协程将没有线程去做其他工作。
Dispatchers.IO实际上默认为 64 个线程。