2. Different approaches to multitasking
All the most widely used languages provide one way or another to create new threads and handle parallel execution of multiple tasks. So, you might ask, what is the fuss with Kotlin’s coroutines? What could the language offer more than the seamless parallel execution of multithreading?
Concurrent execution and its share of problems
Well, first of all, if you have already experimented with concurrent execution (“concurrent” meaning here that multiple execution threads are running in parallel and are able to access the same “resources” at the same time, even if that resource is as simple as a global variable), you probably know what a pain it can be. Precisely because the threads are executing at the same time, you have to be very careful to make sure that another function in another thread will not interfere with what you are trying to do in the current thread. It is not easy to walk when the floor can move under your feet any second. This can lead to a wide range of problems, which are more or less solved by an even wider range or partial and complicated workarounds. If you know about volatile, synchronized, mutexes, thread-safe, reentrant, and so on, you know what I’m talking about, and you might have nightmares about them.
Single-thread is not great either
Another solution is to give up concurrent programming entirely and handle some pseudo-parallelism by hand between your logic tasks, for example with some kind of finite-state machine. That way, you know you won’t have concurrency problems because there is never another function running at the same time as the current one. For some applications it can be the best approach, and it is especially well suited for instance for microcontrollers. However, for a large program, it quickly falls apart and usually leads to a messy and inefficient code that is hard to maintain and doesn’t take advantage of modern multicore CPUs.
An interesting alternative
Coroutines are kind of an intermediate solution between these two approaches, with some interesting twists. They provide a way to write asynchronous code (meaning here that it is not always executed in the order you write it) that is either single or multi-threaded, while simplifying concurrency problems, and making the code shorter and clearer (in some cases, a lot clearer). However, coroutines are not a magic spell that suddenly solve all the issues of concurrent programming for free, and it is therefore important to understand what they do and how they do it in order to use them properly.
Very, very nice article! Many thanks!
It would be very nice to learn more about exception handling in coroutines!
Great article. I love that your examples also cover blocking calls like Thread.sleep and how specifying the dispatcher helps in this case. This would have helped me a lot when I first started working with coroutines!
This is by far one of the BEST introductions to coroutines I’ve read. I’ve shared this with my ream at work.
Thank you for putting this together. The diagrams are amazing! I love how you build intuition, I’ve been wanted to put something like this together for some time but I don’t think I could have done such a good job.
Small nitpick:
In this sentence:
>> “it would only require to change launch() for another coroutine builder : withContext()”
Calling withContext() a coroutine *builder* can be misleading, as doesn’t actually create a new coroutine, it just changes the scheduler (or context should I say). https://pl.kotl.in/T-XZv31xL
>> “This function suspends the current coroutine while the code inside its block is executed.”
Again, there’s only one coroutine 🙂 just in a different context. And the coroutine doesn’t suspend get suspended when calling `withContext` (might suspend later tho)
Hi Fernando, thanks for the comment and the suggestions !
I checked again the doc of withContext and indeed, I had misunderstood its behavior. I think I was mislead by the part that says “suspends until it completes”, which at first I thought meant that the current coroutine was suspended and implicitly that a new one was created. I fixed that paragraph and I hope it is now correct, but please let me know if you see some mistake.
Dude you’re amazing, your prose is completely intuitive and easy to follow. NOW I can read that damned documentation and make sense of it.
Thank you slicia.io for the explanation