挂起函数从InputStream读取

问题描述 投票:1回答:3

我对coroutines很新,因此我想征求意见。

我创建了一个扩展函数来从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
    }
}

从协程的角度来看,你认为我可以改进吗?

kotlin kotlin-coroutines
3个回答
5
投票
while (available() == 0) {
    delay(10)
}

在这里,您希望使用InputStream实现非阻塞IO。你可以想象数据本身会以某种方式“涓涓细流”而你可以等待它变得可用,这样你就可以在随后的read()调用中不加阻塞地获取数据。

这种行为对于任何InputStream都不是通用的。事实上,它可能只适用于SocketInputStream,并且它也存在问题:当远程端关闭连接时,它将继续返回0,直到你再次进行read调用以观察套接字是否已关闭。

InputStream的其他实现中,除非流被缓冲,否则available()将始终返回0,在这种情况下,它将简单地告诉您缓冲区中剩余多少。当缓冲区为空时,输入流实现将不会尝试从底层资源获取任何更多数据,直到您调用read()

因此,我建议至少将函数的接收器缩小到SocketInputStream,但为了完全正确,你应该使用NIO代码。

最后,如果您发现对于您的特定用例,available()循环确实按预期工作并且read()永远不会阻塞,那么您应该删除withContext(IO),因为它意味着两个昂贵的上下文切换(到后台线程和后面)并且其目的仅在于从GUI线程运行阻塞代码。


1
投票

从协同程序的角度来看,你的代码似乎没问题,没什么可改进的。只需从协程构建器调用该函数:启动 - 如果您想要并发或异步 - 如果您想要并行性。例如:

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
}

0
投票

另外Marko的回答我想指出,你不能仅仅通过使用协同程序将你的阻塞代码变成非阻塞代码这并不意味着你不应该使用协同程序。使用它们是有意义的,以获得其他好处:

  1. 它可以保持异步代码的顺序样式。如果您有几个步骤来完成整个任务,则不需要使用特殊的反应类型及其组合器。
  2. 提供扩展任务执行的好方法。在多个任务的情况下,您可以在几个上下文中以及由不同的调度程序在结构上执行它们。

希望这有助于理解整体情况。

© www.soinside.com 2019 - 2024. All rights reserved.