From the kotlin docs
A coroutine is an instance of suspendable computation.
It may suspend its execution in one thread and resume in another one.
delay is a special suspending function.
It suspends the coroutine for a specific time.
Suspending a coroutine does not block the underlying thread, but allows other coroutines to run and use the underlying thread for their code.
When a coroutine is suspended the thread which was running it is free to execute some other coroutine. For example when you use delay()
or callSomeAPI()
they are done asynchronously.
I come from javascript world. There functions like setTimeout()
or fetch()
were executed outside of javascript callstack and inside the browser environment.
But in our kotlin case who exactly is executing those methods? Shouldn't there be a thread which manages stuff? So do we start a new thread to take care of the async code in coroutines?
CodePudding user response:
It really depends on what is the reason to suspend. For example, if we suspend to wait for some IO, then it is probable that underneath another thread is used to block on this IO. Some IO are event-based, so OS notifies our application that the coroutine could be resumed. But in many cases we suspend waiting for another coroutine, for example we wait for its completion or we wait on reading from/writing to a Channel
(suspendable queue). Then, there is no need for additional thread - other coroutines resume our coroutine. delay()
is another example. I'm not sure how does it work internally, it definitely depends on the target platform, but I don't suspect it to do busy-waiting ;-) I guess there is some kind of a timer event provided by the OS.
So once again: it depends.
CodePudding user response:
import kotlinx.coroutines.*
import java.util.concurrent.Executors
suspend fun main(args: Array<String>) {
coroutineScope {
val start = System.currentTimeMillis()
launch(Executors.newSingleThreadExecutor().asCoroutineDispatcher()) { // #1
launch {
println("1 --> : " (System.currentTimeMillis() - start))
Thread.sleep(1000L)
println("1 <--: " (System.currentTimeMillis() - start))
}
launch { // #3
println("2 --> : " (System.currentTimeMillis() - start))
delay(1000L)
println("2 <--: " (System.currentTimeMillis() - start))
}
}
}
coroutineScope {
val start = System.currentTimeMillis()
launch(Executors.newScheduledThreadPool(8).asCoroutineDispatcher()) { // #2
launch {
println("1` --> : " (System.currentTimeMillis() - start))
Thread.sleep(1000L)
println("1` <--: " (System.currentTimeMillis() - start))
}
launch { // #4
println("2` --> : " (System.currentTimeMillis() - start))
delay(1000L)
println("2` <--: " (System.currentTimeMillis() - start))
}
}
}
}
sample output:
1 --> : 56
1 <--: 1063
2 --> : 1063
2 <--: 2086
1` --> : 7
2` --> : 8
1` <--: 1012
2` <--: 1012
watch out for #1 and #2 lines, in #1 we launch coroutine with a single thread executor (something similar to Dispatchers.Main
), so the coroutine are back-ended with a single thread, when we call a blocking function Thread.sleep
, there's no change to start coroutine in #3, so #3 gets invoked after the entire launch,
as for #2, the same thing happen, but there are lots of remaining thread to let #4 gets launched
so
But in our kotlin case who exactly is executing those methods? Shouldn't there be a thread which manages stuff? So do we start a new thread to take care of the async code in coroutines?
not a thread, but a CoroutineContext, e.s.p CoroutineIntercepter
which may defined in the parameter of CoroutineBuilder(lets say ,launch
or something) or the parent scope,
all stuffs are not os/platform specific but user-land scheduler controlled by programmer