Follow my public account “ananzhuo” to learn more knowledge

Coroutines are written in four parts

Coroutine porter – Context and scheduler

Coroutine porter – Composite suspend functions

Coroutine Porter – Cancel with timeout

Coroutine porter – Basic

Coroutines profile

Coroutines are lightweight threads that start with the Launch builder in some Coroutiue contexts. The GlobalScope life cycle is consistent with the entire application life cycle.

Blocking and non-blocking

First the conclusion:

  1. Launch does not block threads
  2. Async can block coroutines or not block coroutines (cannot run directly in a thread, only in a coroutine)
  3. RunBlocking blocks threads
  4. Suspend blocks coroutines (only in coroutines, not threads)
  5. WithContext blocks coroutines (only run in coroutines, not threads)

Launch does not block the thread

  1. code
Fun main() = runBlocking {log(" ready to start coroutine ")// ::A globalscope.launch {log(" run coroutine ")// ::B delay(1)} log(" code running outside the coroutine body ")// ::C  delay(2) }Copy the code
  1. Log output
Log: Ready to start coroutine Log: Code running outside coroutine body Log: Running coroutineCopy the code
  1. conclusion

In this example, the log at C is executed before that at B, so lunch does not block

Async may or may not block

Do not use the await

  1. code
Fun main() = runBlocking {log("A-- async body ")//A val async = GlobalScope. Async {log("B-- async body ")//B delay(1) The log (" C - async method body completes ")} / / C log (" D - async method in vitro to perform ") / / D delay (2)}Copy the code
  1. The log
Log: A-- Async method internal log: D-- Async method external execution log: B-- Async method body log: C-- Async method body execution completeCopy the code
  1. conclusion

Async does not block threads a minor change from the previous example

Using await

  1. code
Fun main() = runBlocking {log("A-- into async method body ")//A val async:Deferred<Unit> = GlobalScope. Async { Log ("D-- async method body ")//B delay(1) log("C--async method body complete ")//C} async.await()// add such a call log("D-- async method body outside ")//D delay(2) }Copy the code
  1. The log
Log: A-- Async method body log: B-- Async method body log: C-- Async method body execution complete log: D-- Async method body execution outsideCopy the code
  1. conclusion

Async blocks the thread with await,

RunningBlock blocks the thread

  1. code
Fun main() {log("A-- enter runningBlock") //A runBlocking {log("B-- enter runningBlock") //B delay(1) Log ("C-- runningBLock")//C} log("D--runBlocking ")//D runBlocking {delay(3)}}Copy the code
  1. The execution result
Log: A-- ready to enter the runningBlock log: B-- enter the runningBlock log: C-- Execute the runningBlock log: D-- execute outside the runBlocking code blockCopy the code
  1. conclusion

In this example, the D outside the runBlocking block is executed after B and C in the runBlocking block are executed in sequence, which proves that the runningBlock blocks the thread.

The suspend function blocks the coroutine

Suspend blocks the coroutine in which it is located, but not the thread, because suspend cannot be called inside a thread

  1. code
Fun main() = runBlocking {log("A-- before suspend function ") wait2Second() log("B-- after suspend function ") delay(3)} suspend fun Wait2Second () {log("C-- call suspend function start ") delay(2) log("D-- call suspend function end ")}Copy the code
  1. The log
Logs: A-- before the suspend function is called; C-- when the suspend function is called; D-- when the suspend function is called; B-- after the suspend function is calledCopy the code
  1. conclusion

The suspend function blocks the coroutine

Whether the withContext blocks the coroutine

  1. code
Fun main() = runBlocking {log("A-- execute withContext") withContext(dispatchers.default){log("B-- execute withContext") delay(1) Log ("C--withContext done ")} log("D-- withContext done ") delay(2)}Copy the code
  1. The log
Log: A-- Log before withContext execution: B-- Start withContext execution log: C--withContext execution completion log: D-- after withContext executionCopy the code
  1. conclusion

WithContext blocks the coroutine, the withContext can only run in the coroutine, not in the thread. In this case, changing Default to Main causes an error because Main is equivalent to running on the Main thread

Waiting for a job

The launch method returns a Job object, which, after calling the Join method, waits for the launch coroutine body to complete before continuing.

The join method can only be called in the coroutine body, not in the thread

  1. code
fun main() = runBlocking {
    log("A")
    val job = GlobalScope.launch {
        log("B")
        delay(2)
        log("C")
    }
    log("D")
    job.join()
    log(E)
}
Copy the code
  1. The log
Log: A log: D log: B log: C log: ECopy the code
  1. conclusion

Join waits for the coroutine body to complete

Structured concurrency

Using GlobalScope.launch is dangerous because it creates a top-level coroutine, and if the launch method performs time-consuming tasks, our objects will leak if they are recycled too soon.

The solution to this situation is to turn on the coroutine in the scope we specify

For example:

  1. code
Fun main() = runBlocking {log(A) // This launch is different from globalscope.launch, It is a launch {log(B) delay(2) log(C)} log(D)} running in the runBlocking scopeCopy the code
  1. The log
Log: A Log: D log: B Log: CCopy the code
  1. Because the launch method runs in the external runBlocking scope,

Scope builder

In addition to having coroutine scopes provided by different builders, you can declare your own scopes using the coroutineScope builder. It creates a coroutine scope and does not end until all initiated coroutines have been executed.

RunBlocking and coroutineScope may look similar because they both wait for their coroutine body and all its child coroutines to end. The main difference is that the runBlocking method blocks the current thread to wait, while the coroutineScope simply hangs, freeing the underlying thread for other purposes. Because of this difference, runBlocking is a normal function and coroutineScope is a suspend function

  1. code
fun main() = runBlocking { // this: CoroutineScope launch {delay(200L) println("Task from runBlocking")} CoroutineScope { Delay (500L) println("Task from nested launch")} delay(100L) println("Task from coroutine scope") // This line will be output before the embedded launch } println("Coroutine scope is over")Copy the code
  1. The output

Advance function refactoring

We can separate the code from launch and async into a suspend function to make it easier to read

Coroutines are very lightweight

Global coroutines are like daemons

The coroutine GlobalScope starts does not keep the process alive

other

  • The delay function does not block the thread, but suspends the coroutine, so delay can only be called in the coroutine