Okay, after trying Coroutines in production I have...
# coroutines
r
Okay, after trying Coroutines in production I have a very noob question about Coroutines. How does Coroutines works internally? What I understood so far is it uses
suspendable lambdas + continuation + yeild
internally. I have also watched Exploring Coroutines in Kotlin by Venkat Subramariam several times. If Coroutines works the way it explained in the video then isn’t it like a single thread managing to execute multiple tasks depending upon the priority? Can we actually execute multiple tasks simultaneously on a single thread using Coroutines or it will be just managed by that thread? By managing I mean by using suspend + yeild, the thread will finish all tasks but time taken to finish all tasks will be combination of all individual taks. Can anyone direct me to better understand internals of Coroutines?
c
By managing I mean by using suspend + yeild, the thread will finish all tasks but time taken to finish all tasks will be combination of all individual taks.
Basically yes. But the value of coroutines isn’t always about the performance. There is a difference between ‘asynchronous’ and ‘parallel/concurrent’. Coroutines help you to construct the program in an asynchronous way more easily. There might be parallelism (or not) between the coroutines created in the program. If so, using multiple threads to drive them is a bonus in the performance. I learned a lot from this video:

https://youtu.be/YrrUCSi72E8

r
Thanks @ckchen
s
@Rohit Surwase Managing threads for coroutines, through `Dispatcher`s is somewhat like how `Scheduler`s do it for Rx. Your coroutine-context contains a dispatcher elelement which is used to run code in coroutines. The dispatcher is, on the JVM, a pool of threads. When a coroutine suspends it will resume the code, after the suspension ends/continues, using the same dispatcher. If that dispatcher is a pool of only one thread, code will resume on that same thread. If that dispatcher is a pool of multiple threads, the code will resume on one of the threads in that pool (usually it will be the same thread that was suspended, but not necessarily so). Whether the dispatcher contains one thread or more than one, the order in which your suspendable code is executed is guaranteed. No need to ‘synchronize’ variables that are local to a coroutine, even if continuation would happen on a different thread. Note that multiple tasks can never be simultaneously executed on one thread.. it is only one thread and one thread can only run one thing at a time.
r
@streetsofboston Thanks for the explanation. What I was trying to understand is doc says single thread can run multiple Coroutines so does it mean that all those Coroutines or few of those Coroutines will be executed simultaneously on the same thread or the thread will just manage to execute those Coroutines using
suspending lambdas + yeild + continuation
?
s
Ah… I would think it is the last one, since no one thread can run more than one thing at a time. A single thread can run parts of multiple and different coroutines in sequence, much like a single thread executor.
r
Ohh… even I think the same but single thread can run multiple Coroutines line confuses me. So, it is more about context/task switching rather than actual improvement in time required for the execution.
s
yep 🙂
b
Single thread can run multiple coroutines because one coroutine will suspend, allowing another to take a turn. When the second one suspends, if the first one is ready for another turn, it will resume. Coroutines use Continuation Passing Style, which means the compiler rewrites your `suspend`ing function at compile time to accept another
Continuation
parameter as the final argument and your return type becomes
Any
to return the final
T
that it originally would return, or a special
COROUTINE_SUSPENDED
constant to signal that the function has suspended execution, but needs another turn later. The continuation holds a "label" which tells the function which part to execute next. To illustrate what this looks like (simplified and not exactly how it's compiled):
Copy code
suspend fun Deferred<Int>.sumWithAsync(val other: Int): Int = this.await() + other
Will be compiled as
Copy code
fun Deferred<Int>.sumWithAsync(val other: Int, val cont: Continuation): Any {
    when (cont.label) {
        1 -> {
            cont.waitFor(this@Deferred)
            cont.label = 2
            return COROUTINE_SUSPENDED
        }
        2 -> {
            val a = this@Deferred.getCompleted()
            return a + other
        }
        else -> error("invalid continuation")
    }
}
again, that's not exactly how a suspending function is compiled (in particular
cont.waitFor
is made up), but it's similar to emphasize how the function does
return COROUTINE_SUSPENDED
so another function can take a turn using a thread
r
Thanks @bdawg.io