Я читаю Kotlin Coroutine и знаю, что он основан на suspend
функции. Но что suspend
значит?
Coroutine или функция приостанавливается?
С https://kotlinlang.org/docs/reference/coroutines.html
По сути, сопрограммы - это вычисления, которые можно приостановить, не блокируя поток.
Я слышал, как люди часто говорят «функция приостановки». Но я думаю, что это сопрограмма, которая приостанавливается, потому что ждет завершения функции? «приостановить» обычно означает «прекратить работу», в этом случае сопрограмма простаивает.
🤔 Следует ли говорить, что сопрограмма приостановлена?
Какая сопрограмма приостанавливается?
С https://kotlinlang.org/docs/reference/coroutines.html
Чтобы продолжить аналогию, await () может быть функцией приостановки (следовательно, также вызываемой из блока async {}), которая приостанавливает сопрограмму до тех пор, пока не будут выполнены некоторые вычисления и не вернет свой результат:
async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}
🤔 В нем говорится, что «это приостанавливает выполнение сопрограммы до тех пор, пока не будут выполнены некоторые вычисления», но сопрограмма похожа на легкий поток. Итак, если сопрограмма приостановлена, как можно выполнить вычисления?
Мы видим, что await
он вызван computation
, возможно async
, он возвращается Deferred
, что означает, что он может запустить другую сопрограмму.
fun computation(): Deferred<Boolean> {
return async {
true
}
}
🤔 В цитате говорится, что выполнение сопрограммы приостанавливается . Означает ли это suspend
внешнюю async
сопрограмму или suspend
внутреннюю computation
сопрограмму?
Означает, suspend
что пока внешняя async
сопрограмма ожидает ( await
) завершения внутренней computation
сопрограммы, она (внешняя async
сопрограмма) бездействует (отсюда и название приостановлено) и возвращает поток в пул потоков, а когда computation
дочерняя сопрограмма завершает работу, она (внешняя async
сопрограмма) ) просыпается, берет другой поток из пула и продолжает?
Причина, по которой я упоминаю эту ветку, связана с https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
Поток возвращается в пул, пока сопрограмма ожидает, и когда ожидание завершено, сопрограмма возобновляет работу на свободном потоке в пуле.
источник
suspend fun
можно поставить на паузу, но как именно?Чтобы понять, что именно означает приостановка сопрограммы, я предлагаю вам просмотреть этот код:
Unconfined
Сопрограммный диспетчер устраняет магию сопрограммной диспетчеризации и позволяет сосредоточиться непосредственно на голых сопрограммах.Код внутри
launch
блока сразу же начинает выполняться в текущем потоке как частьlaunch
вызова. Происходит следующее:val a = a()
b()
, тянетсяsuspendCoroutine
.b()
выполняет переданный блокsuspendCoroutine
и затем возвращает специальноеCOROUTINE_SUSPENDED
значение. Это значение не наблюдается через модель программирования Kotlin, но это то, что делает скомпилированный метод Java.a()
, увидев это возвращаемое значение, сама его также возвращает.launch
Блок делает то же самое и управление теперь возвращается к строке послеlaunch
вызова:10.downTo(0)...
Обратите внимание, что на этом этапе вы получаете такой же эффект, как если бы код внутри
launch
блока и вашfun main
код выполнялись одновременно. Просто так случается, что все это происходит в одном собственном потоке, поэтомуlaunch
блок "приостановлен".Теперь внутри
forEach
кода цикла программа читает,continuation
чтоb()
написала функция, иresumes
принимает значение10
.resume()
реализован таким образом, что это будет так, как если быsuspendCoroutine
вызов вернул значение, которое вы передали. Итак, вы внезапно окажетесь в середине выполненияb()
. Значение, которое вы передали,resume()
присваиваетсяi
и проверяется0
. Если он не равен нулю,while (true)
цикл продолжается внутриb()
, снова достигаяsuspendCoroutine
, после чего вашresume()
вызов возвращается, и теперь вы проходите еще один шаг циклаforEach()
. Это продолжается до тех пор, пока вы, наконец, не продолжите с0
, затем выполняетсяprintln
оператор и программа завершается.Приведенный выше анализ должен дать вам важную интуицию, что «приостановка сопрограммы» означает возвращение элемента управления к самому внутреннему
launch
вызову (или, в более общем смысле, к построителю сопрограмм ). Если сопрограмма снова приостанавливается после возобновления,resume()
вызов завершается, и управление возвращается вызывающей сторонеresume()
.Наличие диспетчера сопрограмм делает эти рассуждения менее четкими, поскольку большинство из них немедленно отправляют ваш код в другой поток. В этом случае описанная выше история происходит в этом другом потоке, и диспетчер сопрограмм также управляет
continuation
объектом, чтобы он мог возобновить его, когда возвращаемое значение станет доступным.источник
Прежде всего, лучший источник для понимания этой IMO - это доклад Романа Елизарова «Глубокое погружение в сопрограммы» .
Вызов приостановить ИНГ функции приостановки S на сопрограмму, что означает текущий поток может начать выполнение другой сопрограмму. Таким образом, считается , что сопрограмма приостановлена, а не функция.
Фактически, по этой причине места вызова функций приостановки называются «точками приостановки».
Давайте посмотрим на ваш код и разберемся, что происходит:
Внешний
async
запускает сопрограмму. Когда он вызываетcomputation()
, внутреннийasync
запускает вторую сопрограмму. Затем вызов toawait()
приостанавливает выполнение внешнейasync
сопрограммы до тех пор, пока выполнение внутреннейasync
сопрограммы не завершится.Вы даже можете увидеть это с помощью одного потока: поток выполнит внешнее
async
начало, затем вызоветcomputation()
и достигнет внутреннегоasync
. В этот момент тело внутреннего async пропускается, и поток продолжает выполнение внешнего,async
пока не достигнетawait()
.await()
это «точка приостановки», потому чтоawait
это функция приостановки. Это означает, что внешняя сопрограмма приостановлена, и, таким образом, поток начинает выполнять внутреннюю. Когда это будет сделано, он возвращается, чтобы выполнить конец внешнегоasync
.Да, именно так.
На самом деле это достигается путем превращения каждой функции приостановки в конечный автомат, где каждое «состояние» соответствует точке приостановки внутри этой функции приостановки. Под капотом функцию можно вызывать несколько раз с информацией о том, с какой точки приостановки она должна начинать выполнение (вам действительно стоит посмотреть видео, которое я связал для получения дополнительной информации об этом).
источник
async
функции JS помечаются таким образом и все же возвращают обещание.Я обнаружил, что лучший способ понять это
suspend
- провести аналогию междуthis
ключевым словом иcoroutineContext
свойством.Функции Kotlin могут быть объявлены как локальные или глобальные. Локальные функции волшебным образом имеют доступ к
this
ключевому слову, а глобальные - нет.Функции Kotlin могут быть объявлены как
suspend
блокирующие или блокирующие.suspend
функции волшебным образом получают доступ кcoroutineContext
свойствам, а блокирующие функции - нет.Дело в том, что
coroutineContext
свойство объявлено как «обычное» свойство в Kotlin stdlib, но это объявление является лишь заглушкой для целей документации / навигации. ФактическиcoroutineContext
это встроенное внутреннее свойство, которое означает, что под капотом компилятора магия знает об этом свойстве, как и о ключевых словах языка.Что
this
ключевое слово делает для локальных функций, так иcoroutineContext
свойство делает дляsuspend
функций: оно дает доступ к текущему контексту выполнения.Итак, вам нужно
suspend
получить доступ кcoroutineContext
свойству - экземпляру текущего контекста сопрограммы.источник
Я хотел дать вам простой пример концепции продолжения. Это то, что делает функция приостановки: она может заморозить / приостановить, а затем продолжить / возобновить. Перестаньте думать о сопрограммах в терминах потоков и семафоров. Подумайте об этом с точки зрения продолжения и даже хуков обратного вызова.
Чтобы было ясно, сопрограмму можно приостановить с помощью
suspend
функции. давайте исследуем это:В android мы могли бы сделать это, например:
Приведенный выше код печатает следующее:
представьте, что он работает так:
Таким образом, текущая функция, из которой вы запустили, не останавливается, просто сопрограмма приостанавливается, пока она продолжается. Поток не приостанавливается с помощью функции приостановки.
Я думаю, что этот сайт может помочь вам разобраться и является моим справочником.
Давайте сделаем что-нибудь классное и остановим нашу функцию приостановки в середине итерации. Мы возобновим его позже
onResume
Сохраните переменную с именем,
continuation
и мы загрузим ее с объектом продолжения сопрограмм:Теперь вернемся к нашей приостановленной функции и заставим ее зависнуть в середине итерации:
Затем где-нибудь еще, например, в onResume (например):
И цикл будет продолжен. Приятно знать, что мы можем заблокировать функцию приостановки в любой момент и возобновить ее по прошествии некоторого времени. Вы также можете посмотреть каналы
источник
Поскольку уже есть много хороших ответов, я хотел бы опубликовать более простой пример для других.
suspend
функцияrunBlocking { }
запускает Coroutine в режиме блокировки. Это похоже на то, как мы блокировали обычные потоки с помощьюThread
класса и уведомляли заблокированные потоки после определенных событий.runBlocking { }
действительно блокирует ток выполняющийся поток, пока сопрограммы (тело между{}
) будет завершенаЭто выводит:
launch { }
одновременно запускает сопрограмму.worker
потоке.worker
и внешний поток (из которого мы вызвалиlaunch { }
) работают одновременно. Внутри JVM может выполнять вытесняющую потоковую передачу.Когда нам требуется, чтобы несколько задач выполнялись параллельно, мы можем использовать это. Есть те,
scopes
которые определяют время жизни сопрограммы. Если мы укажемGlobalScope
, сопрограмма будет работать до истечения времени жизни приложения.Это выводит:
async
иawait
могут помочь.2
функции приостановки myMethod () и myMethod2 ().myMethod2()
должен выполняться только после полного завершенияmyMethod()
ИЛИ, вmyMethod2()
зависимости от результатаmyMethod()
, мы можем использоватьasync
иawait
async
запускает сопрограмму параллельно аналогичноlaunch
. Но он предоставляет способ дождаться одной сопрограммы перед параллельным запуском другой сопрограммы.Это так
await()
.async
возвращает экземплярDeffered<T>
.T
будетUnit
по умолчанию. Когда нам нужно ждать каких - либоasync
завершения «s, нам нужно вызвать.await()
наDeffered<T>
экземпляре чтоasync
. Как и в приведенном ниже примере, мы вызвали,innerAsync.await()
что означает, что выполнение будет приостановлено доinnerAsync
завершения. То же самое мы можем наблюдать на выходе.innerAsync
Будет завершена первая, в которой содержится призывmyMethod()
. А затемasync
innerAsync2
начинается следующий , который вызываетmyMethod2()
Это выводит:
источник