暂停函数读取InputStream

8

我是比较新手的协程使用者,因此想请教一下您的意见。

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

你认为从协程的角度来看,有什么可以改进的地方吗?

3个回答

6
while (available() == 0) {
    delay(10)
}

你希望通过使用InputStream实现非阻塞IO。你想象数据会自己“滴答滴答”地流入,然后你可以等待它变得可用,这样就可以在后续的read()调用中进行拾取而不会阻塞。

然而,这种行为并不适用于所有InputStream。事实上,它可能只适用于SocketInputStream,而且在那里也存在问题:当远程端关闭连接时,它将一直返回0,直到你进行另一个read调用以观察套接字是否关闭。

InputStream的其他实现中,available()总是返回0,除非流已缓冲,在这种情况下,它只会告诉你缓冲区中还剩多少。当缓冲区为空时,输入流实现将不会尝试从底层资源获取任何更多的数据,直到你调用read()

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

最后,如果你发现对于你特定的用例,available()循环按预期工作,read()从不阻塞,那么你应该放弃withContext(IO),因为它意味着两个昂贵的上下文切换(到后台线程和回来),而它的目的仅是在GUI线程之外运行阻塞代码。


我发布的代码是用于从套接字中读取数据的。read() 操作的问题在于它会阻塞并且无法取消。我将在withTimeout子句中使用发布的代码,这就是为什么需要延迟10毫秒的原因。 - Krokodylowy
1
据我所知,无论如何,available()机制在任何情况下都是无用的。它从未对我起过作用,而且我尝试了多次。您应该使用非阻塞网络IO并在“Main”调度程序中运行。 - Marko Topolnik
我认为这完全取决于一个人想如何使用inputStream。在我的情况下,我将一直读取,直到找到我感兴趣的信息(或读取超时)。不会一直读取直到套接字关闭。 此外,根据文档,available()返回的数字不会阻塞读取操作。 - Krokodylowy

2

从协程的角度来看,您的代码似乎没有问题,没有什么需要改进的。只需从协程构建器中调用函数:launch - 如果您想要并发性或async - 如果您想要并行性。例如:

yourScope.launch {

    val inputStream = BufferedInputStream(FileInputStream("filename"))
    val result = inputStream.use {
        it.readData()
    }

    // use ByteArray result
}

此外,您可以通过使用buffer替换return@withContext 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. 提供了一种很好的方式来扩展任务执行。在多个任务的情况下,可以在几个上下文中结构化地执行它们,并由不同的调度程序执行。
希望这能帮助你理解整个情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接