Сопрограммы - это совершенно отдельная вещь от любой политики планирования, которую вы описываете.Сопрограмма - это, по сути, цепочка вызовов suspend fun
s.Подвеска полностью под вашим контролем: вам просто нужно позвонить suspendCoroutine
.Вы получите объект обратного вызова, чтобы вы могли вызвать его метод resume
и вернуться туда, где вы приостановили.
Вот некоторый код, в котором вы можете увидеть, что приостановка - это очень прямой и прозрачный механизм, полностью находящийся под вашим контролем.control:
import kotlin.coroutines.*
import kotlinx.coroutines.*
var continuation: Continuation<String>? = null
fun main(args: Array<String>) {
val job = GlobalScope.launch(Dispatchers.Unconfined) {
while (true) {
println(suspendHere())
}
}
continuation!!.resume("Resumed first time")
continuation!!.resume("Resumed second time")
}
suspend fun suspendHere() = suspendCancellableCoroutine<String> {
continuation = it
}
Сопрограмма, которую вы launch
приостанавливает при каждом вызове suspendHere()
.Он записывает обратный вызов продолжения в свойство continuation
, а затем вы явно используете это продолжение для возобновления сопрограммы.
Код использует диспетчер сопрограмм Unconfined
, который вообще не отправляет потоки, он простозапускает код сопрограммы прямо там, где вы вызываете continuation.resume()
.
Имея это в виду, давайте вернемся к вашей диаграмме:
GlobalScope.launch { <---- (A)
val y = loadData() <---- (B) // suspend fun loadData()
println(y) <---- (C)
delay(1000) <---- (D)
println("completed") <---- (E)
}
- У Котлина естьпредварительно определены
ThreadPool
в начале.
Может иметь или не иметь пул потоков.Диспетчер пользовательского интерфейса работает с одним потоком.
Обязательным условием для того, чтобы поток был целью диспетчера сопрограмм, является наличие параллельной очереди, связанной с ним, и поток запускает цикл верхнего уровня, который берет Runnable
объекты из этой очереди и выполняет их,Диспетчер сопрограмм просто помещает продолжение в эту очередь.
На (A)
Kotlin начинает выполнение сопрограммы в следующей доступной бесплатной теме (скажем, Thread01
).
Это также может быть та же нить, где вы назвали launch
.
При (B)
Kotlin прекращает выполнение текущего потока и запускает функцию приостановки loadData()
в следующем доступном свободном потоке (Thread02
).
Kotlin не имеетнужно остановить любые потоки, чтобы приостановить сопрограмму.На самом деле основной смысл сопрограмм заключается в том, что потоки не запускаются или останавливаются.Цикл верхнего уровня потока продолжится и выберет еще один работающий объект.
Кроме того, сам факт, что вы называете suspend fun
, не имеет значения.Сопрограмма приостанавливается только тогда, когда она явно вызывает suspendCoroutine
.Функция также может просто возвращаться без приостановки.
Но давайте предположим, что она действительно вызвала suspendCoroutine
.В этом случае сопрограмма больше не работает в любом потоке .Он приостановлен и не может продолжаться до тех пор, пока какой-нибудь код не вызовет continuation.resume()
.Этот код может выполняться в любом потоке в любое время в будущем.
Когда (B)
возвращается после выполнения, Kotlin продолжает сопрограмму в следующей доступной свободной теме (Thread03
).
B
hasn 't «возврат за исполнением», сопрограмма возобновляется, пока она находится внутри своего тела.Он может приостановить и возобновить любое количество раз, прежде чем вернуться.
(C)
выполняется Thread03
. В (D)
Thread03
останавливается. Через 1000 мс (E)
выполняется в следующем свободном потоке,скажем Thread01
.
И снова ни одна тема не останавливается.Сопрограмма приостанавливается, и для планирования ее возобновления через 1000 мс используется механизм, обычно специфичный для диспетчера.В этот момент он будет добавлен в очередь выполнения, связанную с диспетчером.
Для конкретности давайте рассмотрим несколько примеров того, какой код требуется для отправки сопрограммы.
Диспетчер пользовательского интерфейса Swing:
EventQueue.invokeLater { continuation.resume(value) }
Диспетчер пользовательского интерфейса Android:
mainHandler.post { continuation.resume(value) }
Диспетчер службы ExecutorService:
executor.submit { continuation.resume(value) }