协程挂起函数和阻塞调用

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 acceptaccept 函数做了很多事情,所有事情都发生在同一个线程上。 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,协程同步运行,协程挂掉的时候其他协程如何不被挂掉。

Kotlin 协程挂起函数 suspend 关键字

Kotlin协程基础介绍--协程(Coroutines)是一种轻量级的线程

Kevin Learn Kotlin--> 协程

Kotlin协程的简单用法(GlobalScopelifecycleScopeviewModelScope)