I have been studying coroutines recently. What is a coroutine. Beyond the scope of this article. We’ll talk about that later. Difference between coroutines and threads. This article mainly introduces the start mode of coroutine. Yes, you heard me right. Coroutines also have boot mode
The construction method of coroutines
Take a look at the coroutine constructor, whose second argument is the start mode. There is also a DEFAULT mode, DEFAULT
public fun CoroutineScope.launch(
// Context callers
context: CoroutineContext = EmptyCoroutineContext,
// Start mode
start: CoroutineStart = CoroutineStart.DEFAULT,
// Coroutine method
block: suspend CoroutineScope. () - >Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
Copy the code
Startup mode Type
Click on CoroutineStart and the enumeration type of the startup mode is DEFAULT
public enum class CoroutineStart {
// After the coroutine is created. Start dispatch immediately. If the coroutine is cancelled before scheduling. Enter the state of canceling the response directly
DEFAULT,
// We only execute when we need it, otherwise we don't.
LAZY,
// Start scheduling immediately, the coroutine will not respond to cancellation until the first point of suspension
ATOMIC,
// The coroutine is created and executed immediately in the call stack of the current function. Until the first hang point is encountered.
UNDISPATCHED
}
Copy the code
To understand the startup mode, there are a few concepts to understand
- Scheduling does not mean execution.
The scheduling here is actually talking about the first parameter. Context CoroutineContext. For the moment, you can think of it as a queue to which coroutine tasks are added. It doesn’t mean it’s executed.
- There is time between scheduling and execution
- Coroutines can be cancelled
DEFAULT
Check the DEFAULT. Website introduction
Once the coroutine is created. Start dispatch immediately. If the coroutine is cancelled before scheduling. Enter the state of canceling the response directly
Let’s start with a picture drawn by the author. I’ll explain it later
Once the coroutine is created. Immediately start dispatching the understanding of this sentence. It’s interesting. Coroutine tasks are added to the scheduler, also known as queues. The start() or join() task triggers itself without having to go there. Here’s the test code. Whether or not to start() has no effect on the test results.
If the coroutine is cancelled before scheduling. Go straight to the cancel response state, which is even more interesting. Start by creating a coroutine task and adding it to the queue. The task will trigger itself. The timing of cancel is difficult to determine, whether before or after the task is executed, even after the coroutine is created. Cancel immediately, or maybe the scheduler is already executing a block of coroutine code.
In explaining the following test code, the author in order to obtain multiple results. After 100 runs, delay(100) is a suspend function, which will be explained later. Sleep (20) is on the thread executing the method. Block for 20 milliseconds. The purpose is to ensure that tasks are added to the scheduler.
private fun todoModuleDefault(
start: CoroutineStart = CoroutineStart.DEFAULT,
isStart: Boolean = false
) {
for (i in 1.100.) {
val buffer = StringBuffer()
buffer.append("start")
val job = GlobalScope.launch(start = start) {
buffer.append("-> Execute coroutine")
delay(100)
buffer.append("-> Coroutine execution completed")
}
buffer.append("-> Start coroutine")
// Whether to start does not affect the result
if (isStart) {
job.start()
}
// Make sure start() is executed
sleep(20)
buffer.append("-> Remove coroutine")
job.cancel()
buffer.append("->end")
println("The first${i}time$buffer")}}Copy the code
The result is as follows. The order in which coroutines are started and executed changes. As we said above. The coroutine task is added after the scheduler. It will be executed immediately. It doesn’t matter if you have start() or not and who comes first depends on the state of the scheduler at the time. Out of control.
The cancel() method is executed after 20 milliseconds. The coroutine is already suspended. Delay (100). Cancel () at this point does not complete the print coroutine execution. It also proves that coroutines can be cancelled.
The first13Start -> start-> execute coroutine -> remove coroutine ->end17Start -> Execute coroutine -> Start coroutine -> remove coroutine ->endCopy the code
LAZY
LAZY
We execute when we need him to execute, or we don’t execute. If it’s canceled before it’s scheduled. Then go straight to the abnormal end state
Start () or join() will not start until it is executed. Change the above test code to LAZY mode. The isStart variable is changed to true and the result is as follows
Start -> Start -> execute coroutine -> remove coroutine ->endCopy the code
The test results for false without start() are as follows
Start -> Start -> remove ->endCopy the code
Obviously. Whether to perform coroutines. It all depends on whether I start()
If it’s canceled before it’s scheduled. Then you go straight to the exception end state, which means you add the coroutine task to the scheduler. Waiting to be executed. At this point it will cancel. Then the coroutine is not executing.
🌰 for example
Take a look at the following example. Start () after intentionally cancel()
private fun todoModuleLazyCancel(
start: CoroutineStart = CoroutineStart.LAZY
) {
for (i in 1.100.) {
val buffer = StringBuffer()
buffer.append("start")
val job = GlobalScope.launch(start = start) {
buffer.append("-> Execute coroutine")
delay(100)
buffer.append("-> Coroutine execution completed")
}
buffer.append("-> Remove coroutine")
job.cancel()
// Make sure cancel() is executed
sleep(20)
buffer.append("-> Start coroutine")
job.start()
buffer.append("->end")
println("The first${i}time$buffer")}}Copy the code
As a result, the coroutine will no longer be executed.
Start -> Remove coroutine -> Start coroutine ->endCopy the code
ATOMIC
ATOMIC
Scheduling starts immediately, and cancellations are not responded to until the coroutine reaches the first hang point
A suspend point is the point at which the suspend function, the method function decorated by suspend, is executed. In the example, delay is a suspend function
He’s kind of like DEFAULT. They all start dispatching directly. Don’t go to start() or join().
The difference between DEFAULT and DEFAULT is that it does not respond to cancellation until the first suspension point is reached. DEFAULT does not require cancellation at any time.
🌰 for example
If you change the schema to ATOMIC, whether or not you start() will not affect the result
private fun todoModuleAtomic(
start: CoroutineStart = CoroutineStart.ATOMIC,
isStart: Boolean = false
) {
for (i in 1.100.) {
val buffer = StringBuffer()
buffer.append("start")
val job = GlobalScope.launch(start = start) {
buffer.append("-> Execute coroutine")
delay(100)
buffer.append("-> Coroutine execution completed")
}
buffer.append("-> Start coroutine")
// Whether to start does not affect the result
if (isStart) {
job.start()
}
// Make sure start() is executed
sleep(20)
buffer.append("-> Remove coroutine")
job.cancel()
buffer.append("->end")
println("The first${i}time$buffer")}}Copy the code
The result is as follows: This is decided because cancel() is not responded to before the first hang point. Coroutines must be executed
2021-01-19 20:50:58.780I: the first3Start -> Start -> execute coroutine -> remove coroutine ->end2021-01-19 20:50:58.982I: the first4Start -> Execute coroutine -> Start coroutine -> remove coroutine ->endCopy the code
UNDISPATCHED
The coroutine is created and executed immediately in the call stack of the current function. Until the first hang point is encountered.
At first glance it looks very similar to ATOMIC. Take note. This is on the call stack of the current function. What does that mean? The thread in which you execute the coroutine. Take the following example. I did it on the main thread. So before the first hanging point. That’s before delay(20). Also on the main thread. The ATOMIC is the thread created in the scheduler.
private fun todoModuleUnDispatched(
start: CoroutineStart = CoroutineStart.UNDISPATCHED,
isStart: Boolean = false
) {
for (i in 1.100.) {
val buffer = StringBuffer()
buffer.append("start")
val job = GlobalScope.launch(start = start) {
buffer.append("-> Execute coroutine A")
buffer.append("[${Thread.currentThread().name}]. "")
delay(20)
buffer.append("-> Execute coroutine B")
buffer.append("[${Thread.currentThread().name}]. "")
delay(100)
buffer.append("-> Coroutine execution completed")
}
buffer.append("-> Start coroutine")
// Whether to start does not affect the result
if (isStart) {
job.start()
}
// Make sure start() is executed
sleep(50)
buffer.append("-> Remove coroutine")
job.cancel()
buffer.append("->end")
println("The first${i}time$buffer")}}Copy the code
Logs in UNDISPATCHED mode. In the main thread
Start -> Execute coroutine A[main]-> Start coroutine -> execute coroutine B[DefaultDispatcher-worker-1]-> delete ->endCopy the code
Log in LAZY mode, in background thread.
Start -> Start coroutine -> execute coroutine A[DefaultDispatcher-worker-2]-> Execute the coroutine B[DefaultDispatcher-worker-1[DefaultDispatcher-worker-]-> end start-> start-> execute coroutine A[DefaultDispatcher-worker-1]-> Execute the coroutine B[DefaultDispatcher-worker-1]-> delete ->endCopy the code
One more thing. Because UNDISPATCHED, the coroutine is created immediately in the call stack of the current function. So his coroutine must also be executed. So ah. In the four modes, LAZY and UNDISPATCHED are the two total start modes that have coroutines executed.
The last
Coroutines are an interesting thing. The author here may not understand comprehensively. There is an error. Please correct me. The wrong article will mislead more people.
I said earlier that the scheduler is a queue. It’s not accurate. Just to make it clear what each mode of activation is. I came up with an alternative. Do not directly task scheduler CoroutineContext is the queue.
Ok, so that’s the startup mode for coroutines.
reference
Cracking Kotlin coroutines (2) – Coroutines starter