This article is based on the official documentation of coroutines, which can be found here.

Print the coroutine name and the corresponding thread

The following logs are displayed:

1.1 Java program processing in AS:

A. open coroutines Debug switch: click on the Edit Configurations, add – Dkotlinx. Coroutines. Debug = on

B. Thread.currentthread (). Name is displayed during log printing

fun printMsg(msg:String){
   println("${Thread.currentThread().name} "+msg)
}
Copy the code

1.2 Android application processing in AS:

The debugging of coroutines can be enabled in the onCreate method of the Application. Thread.currentthread (). Name is displayed in log printing.

class App : Application(a){
    override fun onCreate(a) {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            System.setProperty("kotlinx.coroutines.debug"."on")}}}Copy the code

2. Coroutine foundation

2.1, “hello” first printed 🌰

Open a new child coroutine and set a new dispatchers. IO

fun main(a)= runBlocking {
    launch(Dispatchers.IO) {
        printMsg("wowo")
        delay(1000)
        printMsg("world")
    }
    printMsg("hello")}Copy the code

parsingA: runBlocking is simply a bridge from non-coroutine to coroutine. It launches a coroutine (parent coroutine) and launches a child coroutine (new IO scheduler). That child coroutine is blocked when it is created and blocked again when delay suspends and resumes.

Q: Then why is “hello” printed first and the rest later? In general, we assume that hello must print first in the main thread, and that the child coroutine will print after it is dispatched to an asynchronous thread. Isn’t it? Let’s try a new scheduler instead

fun main(a)= runBlocking {
    launch {  // Inherit the context of the parent coroutine directly
        printMsg("wowo")
        delay(1000)
        printMsg("world")
    }
    printMsg("hello")}Copy the code

Why print Hello first when it’s already in the main thread? Show Kotlin byteCode to find out. The parent coroutine is a nested callback. If the parent coroutine is started, resume(Unit) will be called and invokeSuspend will be called. Case 0 in the outer callback will be executed later. (Based on the current situation, I could not find the time when the CREATE and invoke methods are called, the logic is not self-consistent.) So “Hello” is printed first.

2.2 force “hello” to be printed after 🌰

2.2.1. Use coroutineScope

fun main(a)= runBlocking {
    coroutineScope {
        launch(Dispatchers.IO) {
            printMsg("wowo")
            delay(1000)
            printMsg("world")
        }
    }
    printMsg("hello")}Copy the code

Or:

fun main(a) = runBlocking {
    doWork()
    printMsg("hello")}suspend fun doWork(a) = coroutineScope {
    launch(Dispatchers.IO) {
        printMsg("wowo")
        delay(1000)
        printMsg("world")}}Copy the code

Also take a look at Show Kotlin byteCode. Whenever the coroutineScope package is suspended, let the suspension (or suspension function) go first.

if (CoroutineScopeKt.coroutineScope(var10000, this) == IntrinsicsKt.getCOROUTINE_SUSPENDED()) { 
      return IntrinsicsKt.getCOROUTINE_SUSPENDED(); 
}
Copy the code

2.2.2. Implement it using Join

fun main(a) = runBlocking {
    val job = launch(Dispatchers.IO) {
        printMsg("wowo")
        delay(1000)
        printMsg("world")
    }
    job.join()
    printMsg("hello")}Copy the code

Join () is a suspend function. If a join() function can be suspended, let the join job finish first.

if (job.join(this) == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
   return IntrinsicsKt.getCOROUTINE_SUSPENDED();
}
Copy the code

Coroutines are structured concurrency

Structured concurrency in coroutines means that there is structure between parent and child coroutines. As long as one child coroutine is not finished, the parent coroutine will not end. In a sense, the child coroutine is more like a local variable of the parent coroutine, and when the parent coroutine scope ends, the child coroutine must be finished. The important thing to understand is that when you write coroutine in Kotlin, don’t think that just because the last line of code, “Hello,” is done, it means that the program is done, not necessarily.

4. Coroutines are lightweight

Every time officials say this, they point to repeat and delay as examples

fun main(a) = runBlocking {
    repeat(10000) {
        launch(Dispatchers.Default) {
            delay(1000)
            printMsg("hello2")
        }
    }
    printMsg("hello1")}Copy the code

Open up 10,000 coroutines to do the job, and do it fast. Coroutine delay corresponds to the sleep of the Thread. The former actually executes the task regularly with less Thread switching, while the latter makes the Thread wait for 1s to run again.