【问题标题】:How to use a Kotlin coroutine to call a blocking function?如何使用 Kotlin 协程调用阻塞函数?
【发布时间】:2021-01-26 02:09:11
【问题描述】:

我想使用 Kotlin 协程来调用阻塞函数。

例如,我想要一个挂起函数,它会“阻塞”直到用户响应模态对话框,或者直到网络调用返回,此时对话框和网络库不提供 suspend 函数。

我使用Mutex 实现了它。

例如,对于阻塞网络调用的场景,我做了这样的事情:

class Connection {
    private val mutex = Mutex()
    
    public suspend fun receive(): ByteArray {
        mutex.lock()

        val buf = ByteArray(42)
        
        thread {
            sock.getInputStream().read(buf, 0, 42) // blocking
            mutex.unlock()
        }

        mutex.lock()

        return buf
    }
}

有效。

请忽略上述代码中与网络相关的问题(如错误处理、检查实际读取的字节数等)。

还请忽略使用线程的性能方面(上面的专用线程只是为了举例)。

从使用Mutex 将阻塞流“转换”为协程流的角度来看:

  1. 是否有标准或更好的方法来做到这一点?
  2. 除了网络或性能 - 您认为我的方法有什么缺点吗?

【问题讨论】:

  • 你的意思是像runBlocking 这样会阻塞线程直到操作完成?
  • @R'J 看起来可行:)
  • 阻塞线程并不是真正推荐的使用 Kotlin 协程处理事情的方法。它们非常通用,您可以在没有阻塞线程的情况下获得结果,所以我建议查看下面的人写的内容,特别是使用IO dispatcher

标签: kotlin kotlin-coroutines


【解决方案1】:

你可以这样做:

suspend fun receive(): ByteArray {
    return withContext(Dispatchers.IO) {
        val buf = ByteArray(42)
        sock.getInputStream().read(buf, 0, 42) // blocking
        buf
    }
}

从您的Activity 调用它:

lifecycleScope.launch{
    withContext(Dispatchers.Main) {
        //showLoading
        val result = receive()
        //hideloading
    }
}

receive 函数将在designed for offloading blocking IO tasks to a shared pool of threads 的 IO 调度程序中运行,请参阅有关调度程序here 的更多信息。

【讨论】:

    【解决方案2】:

    标准的方式是使用IO调度器,它被设计成有一个大的线程池来处理阻塞操作(顾名思义,IO)。

    withContext(Dispatchers.IO) {
        sock.getInputStream().read(buf, 0, 42)
    }
    

    这将暂停协程,直到阻塞操作完成。

    您的方法的缺点是执行顺序不完全清楚,并且容易出现死锁。例如,如果您的阻塞调用引发异常,则互斥锁永远不会解锁并且协程将被卡住。此外,它为每个阻塞操作创建新线程,而 IO 调度程序旨在重用线程池以避免这种成本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-27
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      • 2020-04-28
      • 2019-05-31
      • 2020-05-21
      • 1970-01-01
      相关资源
      最近更新 更多