В kotlinx.coroutines
библиотеке вы можете запустить новую сопрограмму, используя launch
(с join
) или async
(с await
). В чем разница между ними?
В kotlinx.coroutines
библиотеке вы можете запустить новую сопрограмму, используя launch
(с join
) или async
(с await
). В чем разница между ними?
Ответы:
launch
используется, чтобы запустить и забыть сопрограмму . Это как начать новую тему. Если код внутри launch
завершается с исключением, то он обрабатывается как неперехваченное исключение в потоке - обычно выводится на stderr в приложениях JVM бэкэнда и приводит к сбою приложений Android. join
используется для ожидания завершения запущенной сопрограммы и не распространяет ее исключение. Однако, аварийный дочерний сопрограмма отменяет своего родителя также с соответствующим исключением.
async
используется для запуска сопрограммы, которая вычисляет некоторый результат . Результат представлен экземпляром, Deferred
и вы должны использовать await
его. Непонятное исключение внутри async
кода хранится в полученном результате Deferred
и не доставляется куда-либо еще, оно будет автоматически отброшено, если не будет обработано. Вы НЕ ДОЛЖНЫ забывать о сопрограмме, которую вы начали с async .
Я считаю это руководство https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md полезным. Я процитирую основные части
🦄 сопрограмма
По сути, сопрограммы являются легкими нитями.
Таким образом, вы можете думать о сопрограмме как о чем-то, что очень эффективно управляет потоком.
🐤 запуск
fun main(args: Array<String>) {
launch { // launch new coroutine in background and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello,") // main thread continues while coroutine is delayed
Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}
Так что launch
запускает фоновый поток, что-то делает и сразу возвращает токен как Job
. Вы можете вызвать join
это, Job
чтобы заблокировать, пока этот launch
поток не завершится
fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch { // launch new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes
}
Yn асинхронный
Концептуально, асинхронность - это как запуск. Он запускает отдельную сопрограмму, которая представляет собой легкую нить, которая работает одновременно со всеми остальными сопрограммами. Разница в том, что запуск возвращает задание и не несет никакого результирующего значения, в то время как async возвращает отложенное - легкое неблокирующее будущее, которое представляет обещание предоставить результат позже.
Так что async
запускает фоновый поток, что-то делает и сразу возвращает токен как Deferred
.
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
Вы можете использовать .await () для отложенного значения, чтобы получить конечный результат, но Deferred также является заданием, поэтому вы можете отменить его, если это необходимо.
Так Deferred
на самом деле Job
. См. Https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
interface Deferred<out T> : Job (source)
Yn асинхронный по умолчанию
Существует опция лени для асинхронизации с использованием необязательного параметра запуска со значением CoroutineStart.LAZY. Он запускает сопрограмму только тогда, когда его результат необходим некоторому ожиданию или если вызывается функция запуска.
launch
и async
используются для запуска новых сопрограмм. Но они исполняют их по-разному.
Я хотел бы показать очень простой пример, который поможет вам понять разницу очень легко
- запуск
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = downloadTask1()
val retVal2 = downloadTask2()
val retVal3 = downloadTask3()
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1}, ${retVal2}, ${retVal3} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask1() : String {
kotlinx.coroutines.delay(5000);
return "Complete";
}
// Task 1 will take 8 seconds to complete download
private suspend fun downloadTask2() : Int {
kotlinx.coroutines.delay(8000);
return 100;
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask3() : Float {
kotlinx.coroutines.delay(5000);
return 4.0f;
}
}
В этом примере мой код загружает 3 данные по нажатию btnCount
кнопки и показывает pgBar
индикатор выполнения, пока не будет завершена вся загрузка. Есть 3 suspend
функции downloadTask1()
, downloadTask2()
и downloadTask3()
которые загружают данные. Чтобы смоделировать это, я использовал delay()
в этих функциях. Эти функции ждет 5 seconds
, 8 seconds
и 5 seconds
соответственно.
Как мы уже использовали launch
для запуска этих функций приостановки, launch
будем выполнять их последовательно (один за другим) . Это означает, downloadTask2()
что запускается после downloadTask1()
завершения и downloadTask3()
запускается только после downloadTask2()
завершения.
Как и на выходном скриншоте Toast
, общее время выполнения для завершения всех 3 загрузок приведет к 5 секундам + 8 секундам + 5 секундам = 18 секундам сlaunch
- асинхронной
Как мы видели, это launch
делает выполнение sequentially
для всех 3 задач. Время для выполнения всех задач было 18 seconds
.
Если эти задачи независимы и если они не нуждаются в результате вычисления другой задачи, мы можем заставить их работать concurrently
. Они запускаются одновременно и работают одновременно в фоновом режиме. Это можно сделать с помощью async
.
async
возвращает экземпляр Deffered<T>
типа, где T
тип данных, который возвращает наша функция приостановки. Например,
downloadTask1()
вернется, так Deferred<String>
как String является типом возвращаемого значения функцииdownloadTask2()
вернется, так Deferred<Int>
как Int является типом возвращаемого значения функцииdownloadTask3()
вернется, так Deferred<Float>
как Float является возвращаемым типом функцииМы можем использовать возвращаемый объект async
типа, Deferred<T>
чтобы получить возвращаемое значение T
типа. Это можно сделать с помощью await()
звонка. Проверьте код ниже, например
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = async(Dispatchers.IO) { downloadTask1() }
val retVal2 = async(Dispatchers.IO) { downloadTask2() }
val retVal3 = async(Dispatchers.IO) { downloadTask3() }
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1.await()}, ${retVal2.await()}, ${retVal3.await()} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
Таким образом, мы запустили все 3 задачи одновременно. Таким образом, мое общее время выполнения будет только то, 8 seconds
что является временем, downloadTask2()
поскольку оно является самым большим из всех трех задач. Вы можете увидеть это на следующем скриншоте вToast message
launch
это для последовательных шуток, а async
для одновременных
launch
и другое async
запустит новые сопрограммы. Вы сравниваете одну сопрограмму без детей с одной сопрограммой с 3 детьми. Вы можете заменить каждый async
вызов на, launch
и абсолютно ничего не изменится в отношении параллелизма.
оба компилятора сопрограмм, а именно launch и async, в основном являются лямбдами с приемником типа CoroutineScope, что означает, что их внутренний блок скомпилирован как функция приостановки, следовательно, они оба работают в асинхронном режиме И оба будут выполнять свой блок последовательно.
Разница между запуском и асинхронностью заключается в том, что они предоставляют две разные возможности. Конструктор запуска возвращает задание, однако асинхронная функция возвращает отложенный объект. Вы можете использовать запуск, чтобы выполнить блок, для которого вы не ожидаете никакого возвращаемого значения, т.е. записи в базу данных или сохранения файла или обработки чего-то, что в основном вызывается для побочного эффекта. С другой стороны, async, который возвращает Deferred, как я уже говорил ранее, возвращает полезное значение из выполнения его блока, объекта, который оборачивает ваши данные, так что вы можете использовать его главным образом для результата, но, возможно, и для его побочного эффекта. NB: вы можете удалить отложенное и получить его значение, используя функцию await, которая будет блокировать выполнение ваших операторов до тех пор, пока не будет возвращено значение или не будут сгенерированы исключения!
оба сопрограммных компоновщика (запуск и асинхронность) могут быть отменены.
что-нибудь еще? Да с запуском, если исключение выдается в его блоке, сопрограмма автоматически отменяется, и исключения доставляются. С другой стороны, если это происходит с асинхронным, исключение не распространяется дальше и должно быть перехвачено / обработано в возвращенном Отложенном объекте.
больше о сопрограммах https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
запуск возвращает работу
async возвращает результат (отложенное задание)
Запуск с объединением используется для ожидания завершения задания. Он просто приостанавливает вызов сопрограммы join (), оставляя текущий поток свободным для выполнения другой работы (например, выполнения другой сопрограммы).
async используется для вычисления некоторых результатов. Он создает сопрограмму и возвращает свой будущий результат в качестве реализации отложенного. Запуск сопрограммы отменяется, когда отменяется результирующая отсрочка.
Рассмотрим асинхронный метод, который возвращает строковое значение. Если асинхронный метод используется без await, он вернет отложенную строку, но если используется await, вы получите строку в результате
Ключевая разница между асинхронностью и запуском. Отложенный возвращает конкретное значение типа T после того, как ваш Coroutine завершит выполнение, а Job - нет.
Async vs Launch Async vs Launch Diff Image
запуск / асинхронный безрезультатно
асинхронный для результата