首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >医生:运行runBlocking的线程阻塞正确吗?

医生:运行runBlocking的线程阻塞正确吗?
EN

Stack Overflow用户
提问于 2022-09-17 13:27:41
回答 2查看 85关注 0票数 0

你的第一个协同线的医生说-

runBlocking的名称意味着运行它的线程(在本例中是主线程)在调用期间被阻塞,直到runBlocking { ... }中的所有协同线完成它们的执行。

运行runBlocking的线程阻塞是否正确?

如果主线程被阻塞了,那么在下面的代码(来自同一个文档)中,启动{}启动的协同线是如何在主线程上运行的呢??

代码语言:javascript
运行
复制
import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println(Thread.currentThread().name + " World!") // print after delay
    }
    println(Thread.currentThread().name + " Hello") // main coroutine continues while a previous one is delayed
}

我的理解是

  • 在runBlocking范围内启动{}创建了一个子协同
  • 而runBlocking则等待这个子协同器的完成,而不像启动{}或异步{}这样的构建器在创建新的协同线之后立即返回。
  • 但是runBlocking释放了主线程
  • 由于主线程是免费的,runBlocking创建的新协同线也运行在主线程上。

以下是上述代码的输出-

代码语言:javascript
运行
复制
main @coroutine#1 Hello
main @coroutine#2 World!

runBlocking文档中的相同问题

运行一个新的协同线,阻塞当前线程的,直到它完成为止。这一功能不应从协同线中使用。它旨在将常规的阻塞代码连接到以挂起方式编写的库中,用于主要函数和测试。

EN

回答 2

Stack Overflow用户

发布于 2022-09-17 15:10:21

比较您为runBlocking发布的文档

运行一个新的协同线并阻塞当前线程,直到它完成为止。

launch

在不阻塞当前线程的情况下启动新的协同线,并以作业的形式返回对协同线的引用。

它们都创建了一个新的协同线,只是launch异步运行协同线,并允许调用方继续运行代码。runBlocking不允许调用者继续运行,直到在该协同线中运行的所有操作完成为止。因此,runBlocking并不控制协同线中的内容的行为,只控制调用runBlocking的代码的执行。

对代码的这种调整可能会使事情变得更清楚:

代码语言:javascript
运行
复制
fun main() {
    runBlocking {
        launch {
            delay(1000L)
            println(Thread.currentThread().name + " World!")
        }
        println(Thread.currentThread().name + " Hello")
    }
    // this -follows- the runBlocking statement
    println("Hey I'm runnin' here!")
}
代码语言:javascript
运行
复制
main @coroutine#1 Hello
main @coroutine#2 World!
Hey I'm runnin' here!

所以现在发生的是:

  • runBlocking运行,阻塞主线程。下一行不会运行,直到这个协同线中的一切都完成!
  • launch运行,在同一个线程上创建一个新的协同线,但不阻塞它。下一行的代码将能够在运行时运行,因为它们位于单独的协同线中,但在短时间内轮流运行(因为它们位于同一个线程上)。这个协同线立即调用delay,所以即使它在原始协同线中运行在行之前( launch)后面的println ),它肯定会在很久之后到达自己的println
  • 跟随printlnlaunch运行,因为这个协同线独立于另一个协同线。这个协同线已经完成(但另一个还在运行)
  • println its launch在延迟后运行。这个协同线没有其他事可做,所以它也完成了
  • 由于由runBlocking发起的协同线中的所有runBlocking都已经完成,所以阻塞协同线结束和执行可以在启动的范围内继续。
  • 最后一个println终于要运行了!

也只是为了澄清一个可能的误解:

运行runBlocking的线程阻塞是否正确? 如果主线程被阻塞,那么启动{}在下面的代码(来自同一个文档)中的coroutine是如何在主线程上运行的?

所有协同器都运行在与调用方相同的线程上,除非明确告诉它们不要运行,或者使用可以将它们移动到不同线程的调度程序。它只是协同“轮流”,并得到一些时间来执行一些线程上的代码。所以看起来他们在“同时”运行,即使他们不是。

因此,由于launch不“阻塞”,这意味着调用它的协同线被允许继续,得到一些时间来执行其余的代码,而启动的协同线也有一些时隙。他们可以独立运行。

但是runBlocking确实会阻塞,这意味着在它启动的协同线完成之前,它不会有任何执行时间。它需要休息直到协同线完成。所以它实际上不是阻塞主线程在正常意义上,否则线程上的任何协同线都不能运行!它只是在它自己的范围内阻止执行--这是一种表达“在这个工作完成之前这里不会发生其他事情”的方式。这就是为什么它是一个常见的main函数包装器,它可以创建一个所有运行的协同工作的世界,并阻止main函数完成和结束程序。但是做的阻止了运行之外的任何东西,这就是为什么在像安卓这样的系统中,除了你想运行的任何协同机制之外,还有其他东西在运行。

而且,launch实际上运行在一个CoroutineScope (即CoroutineScope.launch)上,它可以在一个协同线中访问它--这就是为什么您必须在其中使用它!runBlocking是一个独立的构建器函数,但是传入的函数块有一种CoroutineScope.() -> T类型--也就是说,它将被传递到CoroutineScope,就像框架提供的默认构造器。当它的所有子协同工作完成时,CoroutineScope就是计算出来的,并通知调用者它已经完成了。runBlocking只是等待这样的事情发生,而不是允许执行继续构建器

票数 1
EN

Stack Overflow用户

发布于 2022-09-17 17:41:36

自从我发布了这个问题之后,我搜索了一个答案,并阅读了几篇文章和@cactustictacs的答案,我认为the current thread gets blocked这个词让读者陷入了一种思维障碍,迫使他们认为这条线没有什么作用。

虽然coroutineScope只是挂起,为其他用途释放底层线程,但runBlocking {...}在等待它的身体和所有子协同器完成时,不会离开线程;这就是为什么它是一个正常的函数,而不是挂起的函数。由于所有子协同器都从runBlocking继承相同的上下文(& dispatcher,除非传递另一个上下文),它们将在同一个线程上继续。所以是的,线程被阻塞,等待runBlocking完成,但实际上它正忙着执行runBlocking body及其所有子协同。

另外,来自runBlocking的文档

这个构建器的默认CoroutineDispatcher是事件循环的内部实现,它在这个阻塞的线程中处理连续性,直到这个协同线完成为止。

底层runBlocking的线程正忙于执行一个事件循环,该循环处理runBlocking主体及其所有子协同(那些从runBlocking继承上下文的子协同)。这里有一个漂亮的媒体文章解释了这个幻影事件循环将Kotlin合作伙伴关系学习为Java开发(第二部分)

因此,blocked thread只是意味着线程忙于执行runBlocking主体,除非所有runBlocking完成并启动,否则不会出现。

这和用简单的英语说的话一样--我被阻止研究这个话题,不会再做任何事情了,,除非我解开这个谜团并发布一个关于它的答案。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73755272

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档