Coroutine suspend function and blocking calls
下面是我的用例。
我有一个函数fun requestFocus,此函数依次调用函数fun configure,该函数依赖于系统中的回调,因此此函数configure使用带有计数1的coundownlatch,并等待直到收到回调时将其重置为零。
为此,我已将requestFocus标记为suspend,并使用Dispatchers.IO进行所有操作。
现在,前一个功能fun accept有多个requestFocus调用方。该函数accept做很多事情,并且所有这些事情都发生在同一线程上。函数accept也可以从主线程或意图服务中调用。我的问题是,因为函数configure阻塞了,所以我不想阻塞主线程。
当前接受函数如下所示:
1 2 3 4 5
| fun accept() {
//do something
requestFocus()
// do something
} |
我不确定如何才能从accept调用requestFocus,并确保在requestFocus执行后发生的所有操作都以相同的方式发生。我目前在接受功能中所做的工作如下
1 2 3 4 5 6
| fun accept() {
//do something
runBlocking{
requestFocus()
// do something
} |
但是这会导致问题,因为主线程被阻塞了。有什么建议可以尝试吗?我已经研究了全球范围和主要范围的文档。
- 除在单元测试中外,切勿使用runBlocking。否则,它将完全破坏协程的目的。使用lifecycleScope.launch启动协程。这有点像将Runnable发布到主线程,但是lambda中的代码可以挂起,因此调用suspend函数。因此,如果accept()代表逻辑的完整流程,请使用fun accept() = lifecycleScope.launch { /*...*/ }。
您正在寻找withContext块。 withContext的行为类似于runBlocking,但是它挂起线程而不是阻塞线程。
1 2 3 4 5 6 7 8
| 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创建协程以启动协程:
1 2 3 4 5 6 7 8
| 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
} |
- 谢谢你的帮助。这让我更清楚了。但是,正如我所说,accept可以从主线程以及后台线程中调用。那么有可能在接受线程上执行launch(inside accept)吗?就像您的第二个解决方案一样,它将始终是主线程。所以很好奇它是否可能在协程中
-
有可能,但是如果您想要类似顺序执行,那么使用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()完成。