Учитывая, что сопрограмма выполняется в главном потоке, почему 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 при отсутствии других ограничений (например, сопрограмма блокируется при вводе / выводе). Тем не менее, вы не должны принимать конкретный порядок вызова этих двух сопрограмм.