Порядок исполнения сопрограмм? - PullRequest
1 голос
/ 22 апреля 2020

Я запускаю код A и получаю результат A, запускаю код B и получаю результат B,

Я думаю, что результат B должен быть результатом B +, потому что вызывается b.await(), почему выполнение не было ожидаемым?

Код A

fun main() = runBlocking { 
    val a = async {    
        //delay(200L) 
        println("A1")        
    }    

    val b = async {    
        //delay(100L) 
        println("A2")        
    } 


    //b.await()

    println("A3")
}

Результат A

A3
A1
A2

Код B

fun main() = runBlocking { 
    val a = async {    
        //delay(200L) 
        println("A1")        
    }    

    val b = async {    
        //delay(100L) 
        println("A2")        
    } 


    b.await()

    println("A3")
}

Результат B

A1
A2
A3

Результат B +

A2
A3
A1

Добавленный контент

КСТАТИ, код C Результат B +

Код C

fun main() = runBlocking { 
    val a = async {    
        delay(200L) 
        println("A1")        
    }    

    val b = async {    
        delay(100L) 
        println("A2")        
    } 


    b.await()

    println("A3")
}

Ответы [ 2 ]

1 голос
/ 22 апреля 2020

Позвольте мне разбить код построчно,

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.

1 голос
/ 22 апреля 2020

Похоже, что порядок размещения в коде также влияет на вызовы. Поскольку даже этот код:

fun main() = runBlocking {

    val a = async {
        delay(200L)
        println("A1")
    }

    val b = async {
        delay(200L)
        println("A2")
    }

    b.await()
    a.await()
    println("A3")

}

будет выводить:

A1
A2
A3

Для выхода A2, A3, A1 вы можете использовать:

fun main() = runBlocking {

    val a = async(start = CoroutineStart.LAZY) {
        //delay(200L)
        println("A1")
    }

    val b = async(start = CoroutineStart.LAZY) {
        //delay(200L)
        println("A2")
    }

    b.await()
    println("A3")
    a.await()
}
...