从 InputStream 读取的暂停函数
Suspended function to read from InputStream
我对协程还很陌生,所以我想征求意见。
我创建了一个扩展函数来从 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
}
}
您认为从协同程序的角度来看,我有什么可以改进的地方吗?
从协程的角度来看,您的代码似乎还不错,没有什么可以改进的。只需从协程构建器调用该函数: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
}
while (available() == 0) {
delay(10)
}
在此希望您已经使用 InputStream
实现了非阻塞 IO。您想象数据会以某种方式 "trickling in" 独立存在,您可以等待它变得可用,这样您就可以在随后的 read()
调用中无阻塞地获取它。
这种行为对任何 InputStream
来说并不普遍。事实上,它可能只适用于 SocketInputStream
并且它也有问题:当远程端关闭连接时,它会保持 returning 0
直到你创建另一个 read
调用以观察套接字已关闭。
在 InputStream
的其他实现中,available()
将始终 return 0 除非流被缓冲,在这种情况下,它只会告诉您缓冲区中还剩下多少。当缓冲区为空时,输入流实现不会尝试从底层资源中获取更多数据,直到您调用 read()
.
因此我建议至少将函数的接收者缩小到 SocketInputStream
,但为了完全正确,您应该改用 NIO 代码。
最后,如果您发现对于您的特定用例,available()
循环确实按预期工作并且 read()
从不阻塞,那么您应该删除 withContext(IO)
,因为它意味着两个代价高昂的上下文切换(到后台线程并返回),其目的仅是 运行 阻塞 代码关闭 GUI 线程。
此外,我想指出的是,您不能仅通过使用协程将阻塞代码转换为非阻塞代码这一事实并不意味着您不应该使用协程。使用它们以获得其他好处是有意义的:
- 它可以让异步代码保持顺序风格。如果您有几个步骤来完成整个任务,则不需要使用特殊的反应类型及其组合器。
- 提供了一种扩展任务执行的好方法。在多个任务的情况下,您可以在多个上下文中并由不同的调度程序在结构上执行它们。
希望这有助于理解整个画面。
我对协程还很陌生,所以我想征求意见。
我创建了一个扩展函数来从 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
}
}
您认为从协同程序的角度来看,我有什么可以改进的地方吗?
从协程的角度来看,您的代码似乎还不错,没有什么可以改进的。只需从协程构建器调用该函数: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
}
while (available() == 0) {
delay(10)
}
在此希望您已经使用 InputStream
实现了非阻塞 IO。您想象数据会以某种方式 "trickling in" 独立存在,您可以等待它变得可用,这样您就可以在随后的 read()
调用中无阻塞地获取它。
这种行为对任何 InputStream
来说并不普遍。事实上,它可能只适用于 SocketInputStream
并且它也有问题:当远程端关闭连接时,它会保持 returning 0
直到你创建另一个 read
调用以观察套接字已关闭。
在 InputStream
的其他实现中,available()
将始终 return 0 除非流被缓冲,在这种情况下,它只会告诉您缓冲区中还剩下多少。当缓冲区为空时,输入流实现不会尝试从底层资源中获取更多数据,直到您调用 read()
.
因此我建议至少将函数的接收者缩小到 SocketInputStream
,但为了完全正确,您应该改用 NIO 代码。
最后,如果您发现对于您的特定用例,available()
循环确实按预期工作并且 read()
从不阻塞,那么您应该删除 withContext(IO)
,因为它意味着两个代价高昂的上下文切换(到后台线程并返回),其目的仅是 运行 阻塞 代码关闭 GUI 线程。
此外,我想指出的是,您不能仅通过使用协程将阻塞代码转换为非阻塞代码这一事实并不意味着您不应该使用协程。使用它们以获得其他好处是有意义的:
- 它可以让异步代码保持顺序风格。如果您有几个步骤来完成整个任务,则不需要使用特殊的反应类型及其组合器。
- 提供了一种扩展任务执行的好方法。在多个任务的情况下,您可以在多个上下文中并由不同的调度程序在结构上执行它们。
希望这有助于理解整个画面。