Что означает функция приостановки в Kotlin Coroutine
Я читаю Kotlin Coroutine и знаю, что он основан на suspend
функция. Но что делает suspend
имею в виду?
Сопрограмма или функция приостанавливается?
С 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
сопрограммы) бездействует (отсюда и название suspend) и возвращает поток в пул потоков, а когда дочерний computation
сопрограмма заканчивается, это (внешний async
сопрограмма) просыпается, берет еще один поток из пула и продолжает?
Причина, по которой я упоминаю эту тему, связана с https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
Поток возвращается в пул во время ожидания сопрограммы, а когда ожидание завершено, сопрограмма возобновляется на свободном потоке в пуле
11 ответов
Подвесные функции находятся в центре всего сопрограмм. Функция приостановки - это просто функция, которая может быть приостановлена и возобновлена позднее. Они могут выполнить длительную операцию и ожидать ее завершения без блокировки.
Синтаксис функции приостановки аналогичен синтаксису обычной функции, за исключением добавления ключевого слова suspend. Он может принимать параметр и иметь тип возвращаемого значения. Однако функции приостановки могут быть вызваны только другой функцией приостановки или внутри сопрограммы.
suspend fun backgroundTask(param: Int): Int {
// long running operation
}
Под капотом функции приостановки преобразуются компилятором в другую функцию без ключевого слова suspend, которое принимает дополнительный параметр типа Continuation. Например, приведенная выше функция будет преобразована компилятором в следующее:
fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
// long running operation
}
Продолжение - это интерфейс, который содержит две функции, которые вызываются для возобновления сопрограммы с возвращаемым значением или с исключением, если произошла ошибка во время приостановки функции.
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
Сопрограмма или функция приостанавливается?
Вызов приостановившей функции suspend s сопрограммы означает, что текущий поток может начать выполнение другой сопрограммы. Итак, говорят, что сопрограмма приостановлена, а не функция.
Но технически ваша функция не будет выполняться другой сопрограммой в этот момент, поэтому мы можем сказать, что и функция, и сопрограмма останавливаются, но мы здесь расщепляем волоски.
Какая сопрограмма будет приостановлена?
Внешний async
запускает сопрограмму Когда это вызывает computation()
, внутренний async
начинает вторую сопрограмму. Затем вызов await()
приостанавливает исполнение внешнего async
сопрограммы, до исполнения внутреннего async
сопрограмма окончена.
Вы даже можете увидеть это с помощью одного потока: поток выполнит внешний async
начинай, потом звони computation()
и достичь внутреннего async
, В этот момент тело внутреннего асинхронного пропускается, и поток продолжает выполнять внешнее async
пока не достигнет await()
, await()
является "точкой подвеса", потому что await
это приостановка функции. Это означает, что внешняя сопрограмма приостановлена, и, таким образом, поток начинает выполнять внутреннюю. Когда это сделано, он возвращается, чтобы выполнить конец внешнего async
,
Означает ли приостановка, что в то время как внешняя асинхронная сопрограмма ожидает (ожидает) завершения внутренней сопрограммы вычислений, она (внешняя асинхронная сопрограмма) бездействует (отсюда и имя приостановлено) и возвращает поток в пул потоков, а когда дочерняя сопрограмма вычислений завершает работу, он (внешняя асинхронная сопрограмма) просыпается, берет другой поток из пула и продолжает?
Да, именно так
В качестве учебного пособия я предлагаю вам пройти этот код, который раскрывает базовый механизм, лежащий в основе всех удобных конструкций, таких как async
:
import kotlinx.coroutines.experimental.Unconfined
import kotlinx.coroutines.experimental.launch
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.suspendCoroutine
var continuation: Continuation<Int>? = null
fun main(args: Array<String>) {
launch(Unconfined) {
val a = a()
println("Result is $a")
}
10.downTo(0).forEach {
continuation!!.resume(it)
}
}
suspend fun a(): Int {
return b()
}
suspend fun b(): Int {
while (true) {
val i = suspendCoroutine<Int> { cont -> continuation = cont }
if (i == 0) {
return 0
}
}
}
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
call возвратился со значением, которое вы передали. Таким образом, вы вдруг оказались в середине выполнения b()
, Значение, которое вы передали resume()
получает назначение i
и проверил 0
, Если это не ноль, то while (true)
цикл продолжается внутри b()
снова достигая suspendCoroutine
в какой момент ваш resume()
вызов возвращается, и теперь вы проходите еще один шаг в цикле forEach()
, Это продолжается, пока, наконец, вы не возобновите 0
тогда println
Оператор запускается, и программа завершается.
Приведенный выше анализ должен дать вам важную интуицию, что "приостановка сопрограммы" означает возвращение элемента управления обратно к самому внутреннему launch
вызов (или, в более общем смысле, конструктор сопрограмм). Если сопрограмма снова приостанавливается после возобновления, resume()
Вызов завершается, и управление возвращается вызывающему resume()
,
Присутствие диспетчера сопрограмм делает эти рассуждения менее ясными, потому что большинство из них немедленно отправляют ваш код в другой поток. В этом случае вышеупомянутая история происходит в другой ветке, и диспетчер сопрограмм также управляет continuation
объект, чтобы он мог возобновить его, когда возвращаемое значение доступно.
Поскольку уже есть много хороших ответов, я хотел бы опубликовать более простой пример для других.
Пример использования runBlocking:
- myMethod() - это
suspend
функция runBlocking { }
запускает Coroutine в режиме блокировки. Это похоже на то, как мы блокировали обычные потоки с помощьюThread
класс и уведомление заблокированных потоков после определенных событий.runBlocking { }
действительно блокирует ток выполняющийся поток, пока сопрограммы (тело между{}
) завершаетсяoverride fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name); runBlocking { Log.d(TAG,"Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend"); myMethod(); } Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name); } private suspend fun myMethod() { withContext(Dispatchers.Default) { for(i in 1..5) { Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name); } }
Это выводит:
I/TAG: Outer code started on Thread : main
D/TAG: Inner code started on Thread : main making outer code suspend
// ---- main thread blocked here, it will wait until coroutine gets completed ----
D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- main thread resumes as coroutine is completed ----
I/TAG: Outer code resumed on Thread : main
запуск варианта использования:
launch { }
одновременно запускает сопрограмму.- Это означает, что когда мы указываем запуск, сопрограмма начинает выполнение на
worker
нить. - В
worker
резьба и внешняя резьба (из которой мы вызвалиlaunch { }
) оба работают одновременно. Внутри JVM может выполнять вытесняющую потоковую передачу. Когда нам требуется, чтобы несколько задач выполнялись параллельно, мы можем использовать это. Есть
scopes
которые указывают время жизни сопрограммы. Если мы укажемGlobalScope
, сопрограмма будет работать до истечения времени жизни приложения.override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name); GlobalScope.launch(Dispatchers.Default) { Log.d(TAG,"Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend"); myMethod(); } Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name); } private suspend fun myMethod() { withContext(Dispatchers.Default) { for(i in 1..5) { Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name); } } }
Это выводит:
10806-10806/com.example.viewmodelapp I/TAG: Outer code started on Thread : main
10806-10806/com.example.viewmodelapp I/TAG: Outer code resumed on Thread : main
// ---- In this example, main had only 2 lines to execute. So, worker thread logs start only after main thread logs complete
// ---- In some cases, where main has more work to do, the worker thread logs get overlap with main thread logs
10806-10858/com.example.viewmodelapp D/TAG: Inner code started on Thread : DefaultDispatcher-worker-1 making outer code suspend
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-1
вариант использования async и await:
- Когда у нас есть несколько задач, которые нужно выполнить, и они зависят от выполнения других,
async
а такжеawait
помог бы. - Например, в приведенном ниже коде есть
2
приостановить функции myMethod() и myMethod2().myMethod2()
должен быть выполнен только после полного завершенияmyMethod()
ИЛИmyMethod2()
зависит от результатаmyMethod()
, мы можем использоватьasync
а такжеawait
async
запускает сопрограмму параллельно, аналогичноlaunch
. Но он предоставляет способ дождаться одной сопрограммы перед параллельным запуском другой сопрограммы.Таким образом
await()
.async
возвращает экземплярDeffered<T>
.T
было быUnit
по умолчанию. Когда нам нужно дождаться любогоasync
завершение, нам нужно позвонить.await()
наDeffered<T>
пример этогоasync
. Как и в примере ниже, мы вызвалиinnerAsync.await()
что означает, что выполнение будет приостановлено доinnerAsync
завершается. То же самое мы можем наблюдать на выходе. ВinnerAsync
выполняется первым, что вызываетmyMethod()
. А потом следующийasync
innerAsync2
начинается, который вызываетmyMethod2()
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name); job = GlobalScope.launch(Dispatchers.Default) { innerAsync = async { Log.d(TAG, "Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend"); myMethod(); } innerAsync.await() innerAsync2 = async { Log.w(TAG, "Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend"); myMethod2(); } } Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name); } private suspend fun myMethod() { withContext(Dispatchers.Default) { for(i in 1..5) { Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name); } } } private suspend fun myMethod2() { withContext(Dispatchers.Default) { for(i in 1..10) { Log.w(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name); } } }
Это выводит:
11814-11814/? I/TAG: Outer code started on Thread : main
11814-11814/? I/TAG: Outer code resumed on Thread : main
11814-11845/? D/TAG: Inner code started on Thread : DefaultDispatcher-worker-2 making outer code suspend
11814-11845/? D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- Due to await() call, innerAsync2 will start only after innerAsync gets completed
11814-11848/? W/TAG: Inner code started on Thread : DefaultDispatcher-worker-4 making outer code suspend
11814-11848/? W/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 6 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 7 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 8 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 9 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 10 on Thread : DefaultDispatcher-worker-4
Я хотел дать вам простой пример концепции продолжения. Это то, что делает функция приостановки: она может заморозить / приостановить, а затем продолжить / возобновить. Перестаньте думать о сопрограммах в терминах потоков и семафоров. Думайте об этом с точки зрения продолжения и даже хуков обратного вызова.
Чтобы было ясно, сопрограмму можно приостановить, используя suspend
функция. давайте исследуем это:
В android мы могли бы сделать это, например:
var TAG = "myTAG:"
fun myMethod() { // function A in image
viewModelScope.launch(Dispatchers.Default) {
for (i in 10..15) {
if (i == 10) { //on first iteration, we will completely FREEZE this coroutine (just for loop here gets 'suspended`)
println("$TAG im a tired coroutine - let someone else print the numbers async. i'll suspend until your done")
freezePleaseIAmDoingHeavyWork()
} else
println("$TAG $i")
}
}
//this area is not suspended, you can continue doing work
}
suspend fun freezePleaseIAmDoingHeavyWork() { // function B in image
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)//delay pauses coroutine, NOT the thread. use Thread.sleep if you want to pause a thread.
}
println("$TAG phwww finished printing those numbers async now im tired, thank you for freezing, you may resume")
}
}
}
Приведенный выше код печатает следующее:
I: myTAG: my coroutine is frozen but i can carry on to do other things
I: myTAG: im a tired coroutine - let someone else print the numbers async. i'll suspend until your done
I: myTAG: 1
I: myTAG: 2
I: myTAG: 3
I: myTAG: 4
I: myTAG: 5
I: myTAG: 6
I: myTAG: 7
I: myTAG: 8
I: myTAG: 9
I: myTAG: 10
I: myTAG: phwww finished printing those numbers async now im tired, thank you for freezing, you may resume
I: myTAG: 11
I: myTAG: 12
I: myTAG: 13
I: myTAG: 14
I: myTAG: 15
представьте, что он работает так:
Таким образом, текущая функция, из которой вы запустили, не останавливается, просто сопрограмма приостанавливается, пока она продолжается. Поток не приостанавливается с помощью функции приостановки.
Я думаю, что этот сайт может помочь вам разобраться и является моим справочником.
Давайте сделаем что-нибудь классное и остановим нашу функцию приостановки в середине итерации. Мы возобновим его позжеonResume
Сохраните переменную с именем continuation
и мы загрузим его с помощью объекта продолжения сопрограмм:
var continuation: CancellableContinuation<String>? = null
suspend fun freezeHere() = suspendCancellableCoroutine<String> {
continuation = it
}
fun unFreeze() {
continuation?.resume("im resuming") {}
}
Теперь вернемся к нашей приостановленной функции и заставим ее зависнуть в середине итерации:
suspend fun freezePleaseIAmDoingHeavyWork() {
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)
if(i == 3)
freezeHere() //dead pause, do not go any further
}
}
}
}
Затем где-нибудь еще, например, в onResume (например):
override fun onResume() {
super.onResume()
unFreeze()
}
И цикл будет продолжен. Приятно знать, что мы можем заблокировать функцию приостановки в любой момент и возобновить ее по прошествии некоторого времени. Вы также можете посмотреть каналы
Я обнаружил, что лучший способ понять suspend
провести аналогию между this
ключевое слово и coroutineContext
свойство.
Функции Kotlin могут быть объявлены как локальные или глобальные. Локальные функции волшебным образом получают доступ кthis
ключевое слово, а глобальное - нет.
Функции Kotlin можно объявить как suspend
или блокировка. suspend
функции волшебным образом имеют доступ к coroutineContext
свойство, а функции блокировки - нет.
Дело в том: coroutineContext
свойство объявлено как "обычное" свойство в Kotlin stdlib, но это объявление является лишь заглушкой для целей документации / навигации. по фактуcoroutineContext
- это встроенное внутреннее свойство, которое означает, что под капотом компилятора магия знает об этом свойстве, как и о ключевых словах языка.
какой this
ключевое слово выполняет для локальных функций то, что coroutineContext
собственность делает для suspend
функции: дает доступ к текущему контексту выполнения.
Итак, вам нужно suspend
получить доступ к coroutineContext
свойство - экземпляр текущего выполняемого контекста сопрограммы
Здесь есть много отличных ответов, но я думаю, что есть две дополнительные вещи, которые важно отметить.
launch / withContext / runBlocking и многие другие вещи в примерах взяты из библиотеки сопрограмм. которые на самом деле не имеют ничего общего с приостановкой. вам не нужна библиотека сопрограмм для использования сопрограмм. Сопрограммы - это "уловка" компилятора. Да, библиотека, конечно, упрощает задачу, но компилятор творит чудеса, приостанавливая и возобновляя работу.
Во-вторых, компилятор просто берет код, который выглядит процедурным, и превращает его в обратные вызовы под капотом.
Возьмите следующую минимальную сопрограмму, которая приостанавливает работу и не использует библиотеку сопрограмм:
lateinit var context: Continuation<Unit>
suspend {
val extra="extra"
println("before suspend $extra")
suspendCoroutine<Unit> { context = it }
println("after suspend $extra")
}.startCoroutine(
object : Continuation<Unit> {
override val context: CoroutineContext = EmptyCoroutineContext
// called when a coroutine ends. do nothing.
override fun resumeWith(result: Result<Unit>) {
result.onFailure { ex : Throwable -> throw ex }
}
}
)
println("kick it")
context.resume(Unit)
Я думаю, что важный способ понять это - посмотреть, что компилятор делает с этим кодом. фактически он создает класс для ламбы. он создает свойство в классе для "дополнительной" строки, затем создает две функции: одна печатает "до", а другая - "после".
По сути, вычислитель взял то, что выглядело как производственный код, и превратил его в обратные вызовы.
так что же делает ключевое слово "приостановить"? Он сообщает компилятору, как далеко назад нужно искать контекст, который понадобится сгенерированным обратным вызовам. Компилятору необходимо знать, какие переменные используются в каких "обратных вызовах", и ключевое слово suspend помогает ему. В этом примере "дополнительная" переменная используется как до, так и после приостановки. поэтому его нужно вытащить из класса, содержащего обратные вызовы, которые делает компилятор.
Он также сообщает компилятору, что это "начало" состояния, и о необходимости подготовиться к разделению следующего кода на обратные вызовы. "StartCourtine" существует только при приостановке лямбды.
Фактический код Java, сгенерированный компилятором kotlin, находится здесь. Это оператор switch вместо обратных вызовов, но фактически это то же самое. вызывается сначала с случаем 0, затем с случаем 1 после резюме.
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
var10_2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0: {
ResultKt.throwOnFailure((Object)$result);
extra = "extra";
var3_4 = "before delay " + extra;
var4_9 = false;
System.out.println((Object)var3_4);
var3_5 = this;
var4_9 = false;
var5_10 = false;
this.L$0 = extra;
this.L$1 = var3_5;
this.label = 1;
var5_11 = var3_5;
var6_12 = false;
var7_13 = new SafeContinuation(IntrinsicsKt.intercepted((Continuation)var5_11));
it = (Continuation)var7_13;
$i$a$-suspendCoroutine-AppKt$main$1$1 = false;
this.$context.element = it;
v0 = var7_13.getOrThrow();
if (v0 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended((Continuation)var3_5);
}
v1 = v0;
if (v0 == var10_2) {
return var10_2;
}
** GOTO lbl33
}
case 1: {
var3_6 = this.L$1;
extra = (String)this.L$0;
ResultKt.throwOnFailure((Object)$result);
v1 = $result;
lbl33:
// 2 sources
var3_8 = "after suspend " + extra;
var4_9 = false;
System.out.println((Object)var3_8);
return Unit.INSTANCE;
}
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
Всем, кто все еще сталкивается с этим вопросом, я бы порекомендовал взглянуть на него. Я прочитал много вводящих в заблуждение ответов на этот вопрос, даже некоторые из них с самым высоким рейтингом. Это развеяло многие мои сомнения.
В сопрограммах Kotlinsuspend
Функция — это ключевое слово, используемое для обозначения функции как файла . Аcoroutine
— это легкая потокоподобная структура, которая позволяет последовательно выполнять асинхронные или длительные операции без блокировки основного потока.
Когда функция помечена как приостановка, это означает, что функция может приостановить свое выполнение в определенных точках, не блокируя поток, позволяя запускаться другим сопрограммам. Эти приостановки обычно происходят при ожидании завершения асинхронных операций, таких как сетевые запросы или дисковый ввод-вывод. Вот пример функции, использующей ключевое слово suspend:
suspend fun fetchData(): String {
delay(1000) // Simulating a delay for demonstration purposes [it also suspend function]
return "Data fetched!"
}
какая функция приостановки делает?
1- pause/resume
2- save all variables (save the progress of function when function is pause and resume)
что вам нужно вызвать функцию приостановки?
you need a coroutines scope to call suspend function or another suspend function
тип области действия курутины
1 — lifecycleScope -> (с активностью) = жизненный цикл осознанной активности
2 — viewModelScope -> (с viewModel) = учитывает жизненный цикл viewModel
3 — GlobalScope -> будет работать с областью приложения.
4 — runBlocking -> (не рекомендуется, поскольку создаётся блокирующий поток)
5 - подробнее...
Итак, кого я могу вызвать функцию приостановки с помощью Kotlin? вы вызываете запуск его в 5-м потоке
1 – Основной поток → использование пользовательского интерфейса
2 — поток ввода-вывода -> использование для ввода-вывода
3- Тема по умолчанию
4 - Неограниченная нить
**5 – пользовательская тема -> вы ее создали **
вызвать функцию приостановки 3 вещи
first you need `builder` (Scope)
second `Dispatchers` (for any thread you will work) [Optional]
third `body of scope` you can call suspend here
давайте возьмем пример
fun main(){
GlobalScope.launch(Dispatchers.IO/*[Optional]*/){
// this is body
for(i in 1..10){
Log.i("TEST", fetchData()) // open logcat to see the result
}
}
}
когда вы закончите, сопрограмма вернется в основной поток
Допустим, у нас есть функция с именем myFunction.
fun myFunction(){
Code block 1
Code block 2 //this one has a long running operation
Code block 3
Code block 4
}
Обычно эти блоки кода выполняются как block1, block2, block3, block4. Таким образом, блоки кода 3 и 4 могут выполняться, в то время как блок кода 2 все еще выполняется. Из-за этого могут быть проблемы. (экран может зависнуть, приложение может вылететь)
Но если мы приостановим эту функцию
suspend fun MyFunction(){
Code block 1
Code block 2 //this one has a long running operation
Code block 3
Code block 4
}
Теперь эта функция может быть приостановлена, когда блок кода 2 (длительная операция) начинает выполняться, и возобновляться, когда это делается. После этого будут выполняться блоки кода 3 и 4. Таким образом, не будет неожиданных проблем с совместным использованием потоков.
Для тех, кто все еще задается вопросом, как на самом деле приостановить функцию приостановки, мы используем функцию suspendCoroutine в теле функции приостановки.
suspend fun foo() :Int
{
Log.d(TAG,"Starting suspension")
return suspendCoroutine<Int> { num->
val result = bar()
Log.d(TAG,"Starting resumption")
num.resumeWith(Result.success(result))
}
}
fun bar():Int //this is a long runnning task