Учитывая, что сопрограмма выполняется в основном потоке, почему println («внешняя сопрограмма») ВСЕГДА выполняется первым?
Представим, что вместо этого ваш код был таким:
someView.post {
println("inside post")
}
println("outside post")
Здесь мы создаем Runnable (лямбда-выражение) и передаем его post() на некотором View. post() говорит, что Runnable будет run() в основном потоке приложения ... в конце концов. Этот Runnable помещается в рабочую очередь, которую Looper, питающий основной поток приложения, использует, и он запускается, когда этот Runnable попадает в верхнюю часть очереди (более или менее подробности более беспорядочные IIRC, но здесь не важны).
Но если вы выполняете этот код в основном потоке приложения, println("outside post") всегда будет печататься первым. Runnable помещается в очередь для последующего выполнения, но вы все еще выполняете основной поток приложения, и поэтому, даже если очередь была пустой, этот Runnable не будет выполняться, пока вы не вернете управление основным потоком приложения обратно в Android. Итак, после вызова post() выполнение продолжается с println("outside post").
Под обложками Dispatchers.Main в основном использует post() (опять же, детали более сложны, но не слишком важны для этого обсуждения). Итак, когда вы launch() сопрограмму, это лямбда-выражение ставится в очередь для выполнения в конечном приложении. Но вы уже находитесь в основном потоке приложения, поэтому выполнение продолжается в обычном режиме, и println("outside post") печатается до того, как сопрограмма получит возможность что-либо сделать.
Предположим, что вместо этого ваш код был:
val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
println("inside coroutine")
}
scope.launch {
println("inside another coroutine")
}
Теперь вы находитесь в ситуации, когда теоретически любая из этих строк может быть напечатана первой. Вы ставите в очередь оба лямбда-выражения, и диспетчер должен решить, что запускать в каком потоке в какой момент. На практике меня не удивит, если «внутренняя сопрограмма» всегда будет печататься первой, поскольку простая реализация Dispatchers.Main будет использовать упорядочение FIFO при отсутствии других ограничений (например, сопрограмма блокируется при вводе-выводе). Однако вы не должны предполагать особый порядок вызова этих двух сопрограмм.
person
CommonsWare
schedule
18.10.2019
Dispatchers.Mainработает на основном петлителе. Любая сопрограммаlaunched будет поставлена в очередь на выполнение, поэтому она всегда будет запускаться позже, поскольку у основного цикла сначала будут другие вещи (методы жизненного цикла, обратные вызовы пользовательского интерфейса и т. Д.). - person Pawel   schedule 18.10.2019