协程挂起函数和阻塞调用
Posted
技术标签:
【中文标题】协程挂起函数和阻塞调用【英文标题】:Coroutine suspend function and blocking calls 【发布时间】:2020-06-27 18:35:14 【问题描述】:以下是我的用例。
我有一个函数fun requestFocus
,这个函数又调用函数fun configure
,它依赖于来自系统的回调,因此这个函数 configure 使用计数为 1 的 coundownlatch 并等待直到它在回调时重置为零已收到。
为此,我标记了 requestFocus
暂停并使用 Dispatchers.IO 来完成所有操作。
现在有多个 requestFocus 调用者用于前函数 fun accept
。 accept 函数做了很多事情,所有事情都发生在同一个线程上。 accept 函数也可以从主线程或意图服务中调用。我的问题是因为函数配置被阻塞我不想阻塞主线程。
目前accept函数是这样的
fun accept()
//do something
requestFocus()
// do something
我不确定如何从 accept 调用 requestFocus 并让 requestFocus 执行后发生的所有操作以相同的方式发生。我目前在接受功能中所做的如下
fun accept()
//do something
runBlocking
requestFocus()
// do something
但这会产生问题,因为主线程被阻塞。任何建议我可以尝试什么?我查看了全球范围和主要范围的文档。
【问题讨论】:
除非在单元测试中,否则不要使用runBlocking
。否则它完全违背了协程的目的。使用lifecycleScope.launch
启动协程。这有点像将 Runnable 发布到主线程,但 lambda 中的代码可以挂起并因此调用 suspend
函数。所以如果accept()
代表一个完整的逻辑流,就用fun accept() = lifecycleScope.launch /*...*/
。
【参考方案1】:
您正在寻找withContext
块。 withContext
的行为类似于 runBlocking
,但它暂停线程而不是阻塞它。
suspend fun accept()
//do something on calling thread
withContext(Dispatchers.IO) // Changes to Dispatchers.IO and suspends the calling thread
requestFocus() // Calls requestFocus() on Dispatchers.IO
// do something on Dispatchers.IO
// Resumes calling thread
您需要从协程范围或从另一个挂起函数调用accept
。或者你可以用launch
创建一个协程来启动一个协程:
fun accept() = launch(Dispatchers.Main) // Starts a coroutine on the main thread
//do something on main thread
withContext(Dispatchers.IO) // Changes to Dispatchers.IO and suspends the main thread
requestFocus() // Calls requestFocus() on Dispatchers.IO
// do something on Dispatchers.IO
// Resumes main thread
【讨论】:
感谢您的帮助。这让我更清楚了一些事情。但是,正如我所说,可以从主线程和后台线程调用接受。那么是否有可能在线程接受被调用时启动(内部接受)?就像您的第二个解决方案一样,它将始终是主线程。所以只是好奇它是否可能在协程中 这是可能的,但如果你想要类似顺序的执行,那么使用launch
将不起作用。当您使用launch
创建协程时,您正在创建一个子进程,并且创建该协程的线程不会被挂起,因此它将继续执行,因此accept
可能会在您在其中创建的协程完成之前完成。我的第二个解决方案创建一个协程并开始在主线程上运行,但如果您删除 Dispatchers.Main
,它将创建一个协程并开始在您调用的任何线程上运行 accept
。
谢谢。还有一件事,就像我说的那样,我正在使用 coundownlatch 并在 requestFocus 调用的 configure 方法中等待。我想知道它作为倒计时的好方法是否会保留线程并阻止它。 kotlin-coroutine 中有没有更好的选择。例如,我知道协程有延迟而不是 thread.sleep 与协程一起工作,倒计时用例是否有类似的东西?
您可以使用协程复制 Countdownlatch 行为。例如,创建一个像 val job = launch(start = CoroutineStart.LAZY) ...
这样的惰性协程,并在需要时以 job.start()
启动它。要验证协程是否正在运行,您可以这样做 if(job.isActive) ...
并使用 job.join()
等待协程完成。以上是关于协程挂起函数和阻塞调用的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 协程协程的挂起和恢复 ② ( 协程挂起 和 线程阻塞 对比 )
Kotlin 协程的作用域构建器 coroutineScope与runBlocking 与supervisorScope,协程同步运行,协程挂掉的时候其他协程如何不被挂掉。