如何使用Kotlin协程调用阻塞函数?

3

我希望使用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 这样的东西,它会阻塞线程直到操作完成吗? - Vitaliy-T
@R'J 看起来可以行 :) - obe
阻塞线程并不是使用 Kotlin 协程处理事务的推荐方式。Kotlin 协程非常灵活,您可以在不阻塞线程的情况下实现结果,因此我建议查看下面的内容,特别是使用 IO 调度程序。 - Vitaliy-T
2个回答

3
标准的方法是使用IO调度器,它被设计为拥有大量线程来处理阻塞操作(正如名称所示,IO)。
withContext(Dispatchers.IO) {
    sock.getInputStream().read(buf, 0, 42)
}

此操作将挂起协程,直到阻塞操作完成。

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


3

你只需执行以下操作:

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
    }
}

接收函数将在IO调度程序中运行,该程序旨在将阻塞式IO任务卸载到共享线程池中。有关Dispatcher的更多信息,请点击此处


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