【问题标题】:Why and how Kotlin coroutine prevents blocking of a thread, even without "suspend" keyword?即使没有“suspend”关键字,Kotlin 协程为什么以及如何防止线程阻塞?
【发布时间】:2019-10-20 15:58:55
【问题描述】:

我在 Android 应用中使用协程时遇到了一些意外行为。

假设我有以下函数,它不是“挂起”。它启动工作线程并阻塞调用线程,直到所有工作线程终止:

fun doSomething() : Result {

    // producers init thread
    Thread {
        for (i in 0 until NUM_OF_MESSAGES) {
            startNewProducer(i) // each producer is a thread
        }
    }.start()

    // consumers init thread
    Thread {
        for (i in 0 until NUM_OF_MESSAGES) {
            startNewConsumer() // each consumer is a thread
        }
    }.start()


    synchronized(lock) {
        while (numOfFinishedConsumers < NUM_OF_MESSAGES) {
            try {
                (lock as java.lang.Object).wait()
            } catch (e: InterruptedException) {
                return@synchronized
            }
        }
    }

    synchronized(lock) {
        return Result(
                System.currentTimeMillis() - startTimestamp,
                numOfReceivedMessages
        )
    }

}

我知道(lock as java.lang.Object).wait()很丑,我最好用ReentrantLock,但我故意想掉到最原始的水平。

现在,如果我在没有协程的情况下从 Android 的主线程执行此函数,它会阻塞调用线程(预期行为):

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    someObject.doSomething()
}

但是,如果我只是将它包装在一个也在主线程上执行的协程中,则主线程不再被阻塞,但功能保持不变:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    CoroutineScope(Dispatchers.Main).launch {
        val result = someObject.doSomething()
    }
}

两个问题:

  1. 我认为为了让协程工作,函数应该是“挂起”,但这里不是这样。那么,“暂停”又有什么意义呢?
  2. 调用(lock as java.lang.Object).wait() 应该已经阻塞了主线程。当涉及协程时,它怎么会不呢?协程是否有“拦截”这种低级交互的手段?

谢谢

【问题讨论】:

  • 如果你使用findViewById&lt;View&gt;(android.R.id.content).post { val result = someObject.doSomething() },你会得到同样的结果吗? launch() 与 post() 一样,设置将来要在主应用程序线程上完成的工作,因此当前回调的其余部分可以继续进行(即,在您的情况下,onCreate() 的其余部分)。请参阅this 了解更多信息。
  • @CommonsWare,是的,你是对的。我进一步研究了它,主线程确实被阻塞了。只是动画仍然有效,所以它看起来就像一切都是有生命的,这让我问了这个问题。所以,它与协程无关。能否请您发布一个简短的答案,以便我接受?

标签: android multithreading kotlin kotlin-coroutines


【解决方案1】:

就像View 上的post() 一样,launch()(通常)安排工作相对于当前执行位异步执行。因此,传递给launch() 的 lambda 表达式中的代码最终将在主应用程序线程上运行,就像您提供给 post() 的 Runnable 最终将在主应用程序线程上运行一样。但是,您的 onCreate() 函数将继续超过 launch() 的点以执行它应该执行的任何其他操作。

但是,就像传递给post() 的Runnable 仍然会由于它在run() 中所做的工作而占用主应用程序线程一样,您的协程仍然可以占用主应用程序线程。只是这项工作会比你直接在onCreate() 中进行工作晚。

只是动画仍然有效

IIRC,在较新版本的 Android 上,动画本身在单独的“渲染”线程上进行处理。

【讨论】:

  • 你是对的,它们在渲染线程上得到处理。它的状态可以使用 CPU 分析器轻松跟踪,它应该在主线程旁边可见。
猜你喜欢
  • 2022-07-25
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-29
相关资源
最近更新 更多