Kotlin协程中runBlocking与coroutineScope替换后输出未达预期的行为解析请求
coroutineScope with runBlocking Doesn't Change the Output? Let me break down what's happening here, because your confusion comes from a key detail about how runBlocking behaves depending on where it's called.
First, let's recap your core expectation: you thought the nested runBlocking would block the current thread completely, preventing the top-level launch from running until the nested scope finished. But that's only true when runBlocking is called from a non-coroutine thread. Here's why your code didn't behave that way:
The Critical Difference: runBlocking in Coroutine vs. Non-Coroutine Threads
- When you call
runBlockingfrom a regular thread (like the main thread directly, outside any coroutine), it creates a new blocking event loop, truly blocks the thread, and waits until all coroutines in its scope are done. - But when you call
runBlockingfrom within a coroutine that's already running on a thread managed byrunBlocking, it doesn't block the thread. Instead, it reuses the existing blocking event loop of the parentrunBlocking. This means the thread's event loop can still process other coroutine tasks (like your top-levellaunch) while handling the nestedrunBlocking's work.
Step-by-Step Execution of Your Modified Code
Let's walk through exactly what happens in your updated code:
main()starts the top-levelrunBlocking, which takes over the main thread and creates aBlockingEventLoop.- The top-level
launchis scheduled: it waits 200ms, then prints "Task from runBlocking". This task is added to the main thread's event loop queue. - You call the nested
runBlocking. It detects the existingBlockingEventLoopon the main thread, so it doesn't block the thread—it just submits its own coroutines to the same event loop. - Inside the nested
runBlocking,delay(100L)runs first. After 100ms, it prints "Task from coroutine scope". - At 200ms, the top-level
launch's delay finishes. The event loop processes this task next, printing "Task from runBlocking". - At 500ms, the nested
launch's delay finishes, printing "Task from nested launch". - The nested
runBlockingscope completes (all its child coroutines are done), so execution returns to the top-levelrunBlocking. - Finally, "Coroutine scope is over" is printed.
This is exactly the same flow as the original code with coroutineScope, because the nested runBlocking isn't hogging the thread—it's sharing the event loop with other coroutines.
How to Get Your Expected Output
To see the behavior you initially expected (where the nested runBlocking blocks the thread until it's done), you need to call runBlocking from a non-coroutine thread, separate from the parent runBlocking's thread. For example:
import kotlinx.coroutines.* fun main() = runBlocking { // Launch this coroutine on a separate thread pool launch(Dispatchers.Default) { delay(200L) println("Task from runBlocking") } // Start a new thread and block it with runBlocking Thread { runBlocking { launch { delay(500L) println("Task from nested launch") } delay(100L) println("Task from coroutine scope") } }.join() // Block the main thread until the new thread finishes println("Coroutine scope is over") }
This code will output:
Task from coroutine scope Task from nested launch Task from runBlocking Coroutine scope is over
Here, the nested runBlocking is called from a fresh thread, so it truly blocks that thread until its scope completes. The top-level launch runs on a separate thread pool, so its delay finishes after the nested runBlocking is done.
Key Takeaway You Missed
runBlocking's "blocking" behavior is context-dependent. It only fully blocks the thread when there's no existing coroutine event loop on that thread. When called inside a runBlocking-managed coroutine, it acts more like a scoped suspend function (similar to coroutineScope) because it reuses the existing event loop instead of blocking the thread.
内容的提问来源于stack exchange,提问作者Bahaa




