Каков порядок исполнения с сопрограммами? - PullRequest
0 голосов
/ 18 октября 2019

Рассмотрим следующий код в kotlin.

val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
   println("inside coroutine")
}
println("outside coroutine")

Мы создаем сопрограмму в потоке Main (UI), и после сопрограммы есть некоторый код.
Я знаю, что он мало что даетсмысл делать это в реальном коде, но это всего лишь теоретический вопрос.

Учитывая, что сопрограмма выполняется в главном потоке, почему println ("за пределами сопрограммы") ВСЕГДА выполняется первым?
Я бы ожидал, что иногдая бы увидел сначала вне сопрограммы и другие времена, сначала внутри сопрограммы , вроде как два потока.
Кто (OSили реализация сопрограммы) решает, что коэффициент вне сопрограммы запускается первым?

1 Ответ

1 голос
/ 18 октября 2019

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

...