I started looking at coroutines for the first time in August or September 2021. I was so overwhelmed by the concepts that I gave up. At the end of December 2021, I began to read it again. This time, due to several concepts, I suddenly became able to understand it. In the dream on New Year’s Eve, I put these ideas together again and decided to write them down in my dream.
How to understand what a coroutine is?
I’ve read some articles about coroutines being lightweight threads, but I’ll throw that out in the future. Whatever the definition of coroutines, at least in Kotlin/Android, don’t even think about it.
“Coroutines are a concurrent design pattern that you can use on the Android platform to simplify code that executes asynchronously,” the Android Developer website says. Let me explain this in a very colloquial way:
-
In any case, the Kotlin coroutine code ends up running on a thread, even if the thread changes with dispatchers. IO/Default. It’s just that Kotlin has quietly enabled the thread pool himself.
-
All Kotlin coroutines do is convert asynchronous code into synchronous code. The goal is clear. How to achieve it, forget it.
-
Since everything runs in a thread, what can run in a thread has to be abstracted into a class, because the code is so different. Kotlin coroutines, as a framework, build a framework, define an interface, and then turn code into an implementation of that interface. In Java, only Runnable satisfies this condition. Thread pools can execute(Runnable) and Android.handler can post(Runnable). When it comes to understanding CPS transformations, you can make it much easier to understand them in a Runnable manner.
-
Some people are wondering, what about thread synchronization for coroutines? Again, coroutines are not lightweight threads, they’re not threads, they’re just a way of managing threads, just like a Java thread pool is not threads, it’s thread management, synchronization between your threads is your thread’s business, not thread pools and coroutines. For example, [3] it is said that both thread pools and coroutines ultimately call methods in Runnable, where synchronization is performed. Later, I saw that the coroutine has Mutex, but I didn’t know how to use it, but I think my understanding is ok in principle.
What about scope — structured concurrency, context?
Asynchronous code changes to synchronous form. If there’s going to be a new way to run code that’s different from the old way, it’s going to have to be written differently. How do I know which code you keep and which code you use in a new way? You have to draw me a box, and in this box, you Kotlin can do whatever you want, this is your Kotlin zone, and out of this box, it’s normal.
This special zone is simply called the CoroutineScope CoroutineScope.
Take an example: there is an Internet cafe, originally all customers are directly with the network management interaction, charging money, buy instant noodles…… Now, I told the network management, I built a team, there are 5 people, to open 5 machines to do the task of a game, you have nothing don’t bother us. Now, I partition a Scope out and do whatever I want.
So, structured and concurrent, it means for example: MY recharge has been used up, the network management let us off the plane, then I, the captain will tell the team members, time is up, let’s go, all hurriedly stop the task. When scope.cancel (), all of its subtasks are also stopped.
Scope only identifies an interval. The duration of this interval can be long or short. It can be as long as, for example, I am the owner of an Internet cafe, and no one can turn off my team computer unless the Internet cafe goes bankrupt. It corresponds to GlobeScope, viewModelScope, and lifecycleScope.
I am the context.
Scope is just a Scope. The management of the team should be handled by a specific person. It should be created at the same time as Scope is created.
The most common thing Context does is switch threads. You can think of it as a team member requesting to complete a task in his own way, so the concept of withContext() comes into play:
Scope. Launch (everyone uses AK) {team member A. Shot () Player B. shot () withContext(Player C says he wanted to use the snipe) {player C. Shot ()} player D. Shot () player E. Shot ()}Copy the code
How to understand CPS?
An implementation of asynchronous code in a synchronous form is that the Kotlin compiler performs a CPS transformation on the suspend function. If you find it hard to understand, think of it as Runnable.
If AK is the main thread, skovox is the IO thread, and the previous line of member C is the main thread, this line becomes the IO thread, and the next line becomes the main thread. It cuts back and forth from thread to thread, and it’s serial, so let’s ignore CPS and continuations and translate it into possible pseudocode:
Handler.postRunnable {player a. shoots () player B. Shot () IO thread pool. executeRunnable {member C. Shot () handler. postRunnable {member D. Shooting () Team member E. shooting ()}}}Copy the code
CPS actually does much the same thing, except it uses a different approach, smoothing out the nesting with a Continuation+ state machine. I didn’t really look at the post-CPS code either, just to get the general idea.
Continuation { int state = 0; Runnable r1 = {// Since these two threads are not cut, we use A Runnable to do team A. State = 1; state = 1; I finished (); } IO.Runnable r2 = {// IO thread member C. State = 2; I finished (); } handler. Runnable r3 = {// Since these two threads do not cut, we use a Runnable to do team D. E. Shoot () state = 3; I finished (); } void () {when (state) : 0 -> r1; R2 1 -> r2 1 -> r2 1 -> r2 2 -> r2 2 -> r2 // Assuming it is time-consuming, change the state after the time-consuming task is complete, call this method again, enter R3 2 -> R3; 3 -> Task end}}Copy the code
The Continuation is also created with the Context when the Scope is created, and the same Continutation object is used throughout the Scope. CPS adds this same argument to the suspend method in the Scope. It also has to be the same, because it maintains state, and threads jump around on that state.
Just let it be
I think it’ll make it easier for you to understand coroutines.
And Job, the exception handling mechanism, I don’t have a clue, but when I do, I’ll see if I can explain it in a way that’s less rigorous but easier to understand.