从InputStream

时间:2019-01-10 01:05:10

标签: kotlin kotlin-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
    }
}

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

3 个答案:

答案 0 :(得分:5)

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

您希望在此使用InputStream实现无阻塞IO。您以为数据会以某种方式自行“滴入”,您可以等待它变得可用,这样就可以在不阻塞后续read()调用的情况下进行拾取。

此行为并非任何InputStream都具有。实际上,它可能仅适用于SocketInputStream,并且那里也存在问题:当远端关闭连接时,它将继续返回0,直到您再次调用read为止。观察插座已关闭。

InputStream的其他实现中,available()始终返回0,除非对该流进行了缓冲,在这种情况下,它只会告诉您缓冲区中还剩下多少。当缓冲区为空时,输入流实现将不会尝试从基础资源中获取更多数据,除非您调用read()

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

最后,如果发现对于特定的用例,available()循环确实可以正常工作,并且read()从不阻塞,则应该丢弃withContext(IO),因为这意味着两个代价高昂的环境切换(切换到后台线程并返回),其目的只是在GUI线程之外运行 blocking 代码。

答案 1 :(得分:1)

从协同程序的角度来看,您的代码似乎还可以,没有什么可以改进的。只需从协程生成器中调用该函数:启动-如果您需要并发,或者 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
}

答案 2 :(得分:0)

此外,我想指出Marko的答案,即仅使用协程就不能将阻塞代码变成非阻塞代码这一事实并不意味着您不应该使用协程。为了获得其他利益而使用它们很有意义:

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

希望这有助于理解整个图片。