Dave Leeds
06/25/2024, 3:19 PMwithContext()
creates a new coroutine. My understanding has so far been that it allows you to change the context of the currently-running coroutine, without creating a new one. The docs say that you can "... [use] the withContext function to change the context of a coroutine while still staying in the same coroutine". Similarly, Kotlin in Action 2nd Edition says, "To switch dispatchers for an already existing coroutine, you can use the withContext function and pass a different dispatcher" (p.412)
The debugging information appears to bear this out as well, with this code:
// Run this with JVM option: -Dkotlinx.coroutines.debug
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() {
runBlocking(Dispatchers.IO) {
log("Start runBlocking")
withContext(Dispatchers.Default) { log("Inside withContext") }
launch { log("Inside launch") }
async { log("Inside async") }.await()
log("End")
}
}
This produces the following output, which also indicates that the code within withContext()
is running in the same coroutine as runBlocking()
(note the @coroutine#
ID numbers on each line)
[DefaultDispatcher-worker-1 @coroutine#1] Start runBlocking
[DefaultDispatcher-worker-3 @coroutine#1] Inside withContext
[DefaultDispatcher-worker-2 @coroutine#2] Inside launch
[DefaultDispatcher-worker-3 @coroutine#3] Inside async
[DefaultDispatcher-worker-3 @coroutine#1] End
However, when looking at the source for the withContext()
function, we can see a DispatchedCoroutine
is created, and if we print out the actual coroutineContext
instance in our log:
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun main() {
runBlocking(Dispatchers.IO) {
log("Start runBlocking $coroutineContext")
withContext(Dispatchers.Default) { log("Inside withContext $coroutineContext") }
launch { log("Inside launch $coroutineContext") }
async { log("Inside async $coroutineContext") }.await()
log("End $coroutineContext")
}
}
We get this output:
[DefaultDispatcher-worker-1 @coroutine#1] Start runBlocking [CoroutineId(1), "coroutine#1":BlockingCoroutine{Active}@5335cb3, Dispatchers.IO]
[DefaultDispatcher-worker-2 @coroutine#1] Inside withContext [CoroutineId(1), "coroutine#1":DispatchedCoroutine{Active}@6f1ba73a, Dispatchers.Default]
[DefaultDispatcher-worker-3 @coroutine#2] Inside launch [CoroutineId(2), "coroutine#2":StandaloneCoroutine{Active}@6a356803, Dispatchers.IO]
[DefaultDispatcher-worker-2 @coroutine#3] Inside async [CoroutineId(3), "coroutine#3":DeferredCoroutine{Active}@2ccad1ad, Dispatchers.IO]
[DefaultDispatcher-worker-2 @coroutine#1] End [CoroutineId(1), "coroutine#1":BlockingCoroutine{Active}@5335cb3, Dispatchers.IO]
Here I see that the coroutine ID is preserved inside withContext()
, but it has a different instance (DispatchedCoroutine
rather than BlockingCoroutine
). Also, we can print out the children:
fun main() {
runBlocking(Dispatchers.IO) {
val mainJob = coroutineContext[Job]!!
log("Start runBlocking")
println(mainJob.children.count())
withContext(Dispatchers.Default) {
log("Inside withContext")
println(mainJob.children.count())
}
log("End")
println(mainJob.children.count())
}
}
This shows that the outer job does have one child while withContext()
is running.
[DefaultDispatcher-worker-1 @coroutine#1] Start runBlocking
0
[DefaultDispatcher-worker-3 @coroutine#1] Inside withContext
1
[DefaultDispatcher-worker-3 @coroutine#1] End
0
So - was my initial understanding incorrect? Or imprecise? Or are we considering DispatchedCoroutine
to be an "implementation detail"? Or is it just that a new *Coroutine
(or Job
) object instance doesn't exactly correspond to a new actual running coroutine? Thanks!Abhimanyu
06/26/2024, 2:37 AMwithContext()
, the coroutine context changes for the code with in the with context block.
Hence, we see a different output for coroutine context.
withContext
starts a new Job that also uses the same existing coroutine.
Coroutine1 - Job1 - CoroutineContext1
|
|
withContext() - Coroutine1 - Job2 - CoroutineContext2
Dave Leeds
06/27/2024, 2:59 AMSam
06/28/2024, 2:18 PMCLOVIS
06/28/2024, 2:19 PMSam
06/28/2024, 2:21 PMSam
06/28/2024, 2:29 PM