Позвольте мне разбить код построчно,
fun main() = runBlocking {
val a=async {
//delay(200L)
println("A1")
}
val b= async {
//delay(100L)
println("A2")
}
b.await()
println("A3")
}
Когда выполнение элемента управления начинается с основного метода, оно работает следующим образом:
1. runBlocking
: как только Обнаружено runBlocking
, основной поток заблокирован до тех пор, пока не завершится сопрограмма runBlocking
. runBlocking
содержит CoroutineScope
, и ни одна область действия никогда не заканчивается до тех пор, пока дочерние элементы внутри области действия не будут завершены, т.е. runBlocking
завершится только тогда, когда все дочерние сопрограммы внутри завершатся первыми.
val a = async{}
: при обнаружении асинхронного c он немедленно возвращает отложенное значение, и код внутри асинхронного c не выполняется. Хотя это никогда не упоминается в документации, но создается впечатление, что компилятор внутренне поддерживает механизм, похожий на очередь, в котором он хранит встреченные сопрограммы по позициям, т. Е. Встреченная сопрограмма первой будет отправлена первой. Таким образом, этот async{}
сохраняется в такой очереди и будет основным кандидатом на отправку, когда текущая сопрограмма (runBlocking{}
) будет приостановлена.
val b = async{}
: То же самое относится и к этому блоку async{}
, компилятор не будет выполнять код внутри этого async
, но будет хранить эту сопрограмму в той же загадочной очереди во второй позиции.
b.await()
: как только вызывается b.await()
, текущая сопрограмма runBlocking{}
приостанавливается, а затем диспетчер выбирает самый верхний элемент во внутренней очереди, т.е. в нашем случае val a = async{}
, и обрабатывает его для выполнения основного потока в результате вы видели, как печатается A1
, а не A2
.
Когда сопрограмма val a = async{}
завершена, диспетчер выбрал второй элемент в очереди, который на этот раз был val b= async {}
и обработал его в главном потоке, в результате вы увидели A2
.
Как только val b= async {}
закончил задание b.await()
, т. Е. Чтобы вернуть результат Deferred операция была закончена Кроме того, в очереди не было элементов, поэтому родительская сопрограмма runBlocking{}
возобновила выполнение и, наконец, напечатала вывод A3
.
Поскольку все внутренние дочерние сопрограммы были выполнены, runBlocking{}
было разрешено завершить. Как только runBlocking{}
было завершено, для выполнения основного потока не осталось работы, и, следовательно, программа завершилась.
TL DR; Важное значение имеет позиция декларации coroutines
.