Kotlin 协程为什么以及如何防止线程阻塞,即使没有 "suspend" 关键字?
Why and how Kotlin coroutine prevents blocking of a thread, even without "suspend" keyword?
我在 Android 应用程序中使用协程时遇到了一些意外行为。
假设我有下面的函数,它不是"suspend"。它启动工作线程并应阻塞调用线程,直到所有工作线程终止:
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()
}
}
两个问题:
- 我认为为了协程工作,函数应该是"suspend",但这里不是这样。那么,"suspend" 有什么意义呢?
- 调用
(lock as java.lang.Object).wait()
应该会阻塞主线程。当涉及协程时,为什么不呢?协程是否具有 "intercepting" 如此低级交互的平均值?
谢谢
就像 View
上的 post()
一样,launch()
(通常)安排相对于当前执行位异步执行的工作。因此,传递给 launch()
的 lambda 表达式中的代码最终将在主应用程序线程上变为 运行,就像您提供给 post()
的 Runnable
将是 运行 最终在主应用程序线程上。但是,您的 onCreate()
函数将继续超过 launch()
点以执行它应该做的任何其他事情。
但是,就像传递给 post()
的 Runnable
仍然会因为它在 run()
中所做的工作而占用主应用程序线程一样,您的协同程序仍然会占用主应用程序线程主应用程序线程。只是这项工作会比直接在 onCreate()
.
中完成工作要晚一些。
It's just that animations still work
IIRC,在较新版本的 Android 上,动画本身在单独的 "render" 线程上处理。
我在 Android 应用程序中使用协程时遇到了一些意外行为。
假设我有下面的函数,它不是"suspend"。它启动工作线程并应阻塞调用线程,直到所有工作线程终止:
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()
}
}
两个问题:
- 我认为为了协程工作,函数应该是"suspend",但这里不是这样。那么,"suspend" 有什么意义呢?
- 调用
(lock as java.lang.Object).wait()
应该会阻塞主线程。当涉及协程时,为什么不呢?协程是否具有 "intercepting" 如此低级交互的平均值?
谢谢
就像 View
上的 post()
一样,launch()
(通常)安排相对于当前执行位异步执行的工作。因此,传递给 launch()
的 lambda 表达式中的代码最终将在主应用程序线程上变为 运行,就像您提供给 post()
的 Runnable
将是 运行 最终在主应用程序线程上。但是,您的 onCreate()
函数将继续超过 launch()
点以执行它应该做的任何其他事情。
但是,就像传递给 post()
的 Runnable
仍然会因为它在 run()
中所做的工作而占用主应用程序线程一样,您的协同程序仍然会占用主应用程序线程主应用程序线程。只是这项工作会比直接在 onCreate()
.
It's just that animations still work
IIRC,在较新版本的 Android 上,动画本身在单独的 "render" 线程上处理。