我对协同程序还很陌生,所以我想征求一下意见。
我创建了一个扩展函数来从InputStream
:读取数据
suspend fun InputStream.readData(): ByteArray {
return withContext(Dispatchers.IO) {
while (available() == 0) {
delay(10)
}
val count = available()
val buffer = ByteArray(count)
read(buffer, 0, count)
return@withContext buffer
}
}
你认为从协游的角度来看,我有什么可以改进的吗?
while (available() == 0) {
delay(10)
}
在这里,您希望使用InputStream
实现了无阻塞IO。您可以想象,数据会以某种方式自行"流入",您可以等待它变为可用,这样您就可以在随后的read()
调用中不阻塞地提取它。
这种行为并不适用于任何InputStream
。事实上,它可能只适用于SocketInputStream
,而且它也有问题:当远程端关闭连接时,它将一直返回0
,直到您再次调用read
以观察套接字是否关闭。
在InputStream
的其他实现中,除非流被缓冲,否则available()
将始终返回0,在这种情况下,它只会告诉您缓冲区中还剩多少。当缓冲区为空时,在调用read()
之前,输入流实现不会尝试从底层资源中获取更多数据。
因此,我建议至少将函数的接收器缩小到SocketInputStream
,但为了完全正确,您应该使用NIO代码。
最后,如果您发现对于您的特定用例,available()
循环确实按预期工作,并且read()
从不阻塞,那么您应该放弃withContext(IO)
,因为它意味着两个代价高昂的上下文切换(到后台线程和返回),并且它的目的只是在GUI线程外运行阻塞代码。
从协同程序的角度来看,您的代码似乎还可以,没有什么需要改进的。只需从协同程序生成器调用函数:launch-如果您想要并发,或者async–如果您想要并行。例如:
yourScope.launch {
val inputStream = BufferedInputStream(FileInputStream("filename"))
val result = inputStream.use {
it.readData()
}
// use ByteArray result
}
另外,您可以稍微减少代码,将return@withContext buffer
替换为buffer
,并将withContext(Dispatchers.IO)
移出函数块:
suspend fun InputStream.readData(): ByteArray = withContext(Dispatchers.IO) {
while (available() == 0) {
delay(10)
}
val count = available()
val buffer = ByteArray(count)
read(buffer, 0, count)
buffer
}
除了Marko的回答之外,我想指出的是,不能仅仅通过使用协程将阻塞代码变成非阻塞代码,这并不意味着你不应该使用协程。使用它们来获得其他好处是有意义的:
- 它使异步代码能够保持顺序样式。如果你有几个步骤来完成整个任务,你就不需要使用特殊的反应类型及其组合器
- 提供了一种扩展任务执行的好方法。在多个任务的情况下,您可以在多个上下文中通过不同的调度器以结构方式执行它们
希望这有助于理解整个画面。