Яка різниця між запуском / приєднанням та асинхронізацією / очікуванням в котлінах Котліна


Відповіді:


232
  • launchвикористовується для стрілянини та забуття програми . Це як почати нову нитку. Якщо код всередині launchзакінчується за винятком, то він розглядається як невиконаний виняток у потоці - зазвичай друкується на stderr у додатках JVM та виходить з ладу для програм Android. joinвикористовується для очікування завершення запущеної програми та не поширює її виняток. Однак, збій дочірньої програми скасовує свого батьківського з відповідним винятком.

  • asyncвикористовується для запуску програми, яка обчислює певний результат . Результат представлений екземпляром, Deferredі ви повинні використовувати awaitйого. Невиконане виключення всередині asyncкоду зберігається всередині отриманого результату Deferredі не надходить ніде більше, воно буде беззвучно відкинуто, якщо не буде оброблено. Ви НЕ МОЖЕТЕ забути про програму, яку ви розпочали з асинхронізації .


1
Async - правильний конструктор програм для мережевих дзвінків в Android?
Фарааз

Правильний конструктор корупції залежить від того, що ви намагаєтеся виконати
Роман Єлизаров

9
Чи можете ви детальніше зупинитися на тому, що "НЕ МОЖЕТЕ забути про програму, яку ви почали з асинхронізації"? Чи є такі прийоми, яких не можна було б очікувати, наприклад?
Луїс

2
"Невиконане виключення всередині коду асинхронізації зберігається всередині отриманого" Відкладеного "і не доставляється більше нікуди, воно буде беззвучно відкинуто, якщо не буде оброблено."
Роман Єлизаров

9
Якщо ви забудете результат асинхронізації, він закінчиться і буде зібрано сміття. Однак, якщо він виходить із-за помилки у вашому коді, ви ніколи не дізнаєтесь про це. Ось чому.
Роман Єлизаров

77

Цей посібник 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
}

🦆 асинхронізація

Концептуально асинхрон - це як запуск. Він починає окрему програму, яка є легкою ниткою, яка працює одночасно з усіма іншими процедурами. Різниця полягає в тому, що запуск повертає роботу і не несе жодного результату, а 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насправді є Job. Дивіться https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html

interface Deferred<out T> : Job (source)

🦋 async є нетерплячим за замовчуванням

Існує можливість лінь асинхронізувати за допомогою необов'язкового параметра запуску зі значенням CoroutineStart.LAZY. Він запускає програму лише тоді, коли її очікують потрібні очікувані або якщо викликається функція запуску.


11

launchі asyncвикористовуються для початку нових процедур. Але вони виконують їх по-різному.

Я хотів би показати дуже базовий приклад, який допоможе вам зрозуміти різницю дуже легко

  1. запуск
    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

Приклад запуску

  1. асинхронізація

Як ми бачили, це 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

чекайте прикладу


1
Дякуємо, що launchasync
згадали,

Ви використовували запуск один раз для всіх завдань та асинхронізацію для кожного з них. Можливо, це швидше, тому що кожен був запущений в іншій програмі і не чекає когось? Це неправильне порівняння. Зазвичай продуктивність однакова. Однією з ключових відмінностей є те, що запуск завжди запускає нову програму замість асинхронізації, яка розбиває її власника. Ще один фактор полягає в тому, що якщо одна з завдань асинхронізації не вдасться з тієї причини, батьківська підпрограма вийде з ладу. Ось чому async не так популярний, як запуск.
p2lem8dev

1
Ця відповідь не вірна, порівнюючи асинхронізацію з функціями призупинення безпосередньо замість запуску. Замість виклику призупинення функції безпосередньо, наприклад, якщо ви зателефонуєте на запуск (Dispatchers.IO) {downloadTask1 ()}, ви побачите, що обидва виконуються одночасно, а не послідовно , ви не зможете отримати вихід, але ви побачите, що це не послідовний. Крім того, якщо ви не з'єднаєте deferred.await () і не зателефонуєте deferred.await () окремо, ви побачите, що асинхронізація є послідовною.
фракійський

2
-1 це просто неправильно. І те, launchі asyncінше розпочнуть нові спроби. Ви порівнюєте одну програму, яка не має дітей, і одну програму з 3 дітьми. Ви можете замінити кожну з asyncвикликів launchі абсолютно нічого не зміниться щодо одночасності.
Мойра

Сторонній шум у цій відповіді додає складності, яка знаходиться поза темою спільної роботи.
truthadjustr

6
  1. обидва конструктори програм, а саме запуск та асинхронізація - це в основному лямбда з приймачем типу CoroutineScope, що означає, що їх внутрішній блок складається як функція призупинення, отже вони обидва працюють в асинхронному режимі, і вони обидва будуть виконувати свій блок послідовно.

  2. Різниця між запуском і асинхронією полягає в тому, що вони дають дві різні можливості. Конструктор запуску повертає Job, однак функція async поверне об'єкт Deferred. Ви можете використовувати запуск для виконання блоку, який ви не очікуєте від нього жодного поверненого значення, тобто запис у базу даних або збереження файлу або обробка чогось, в основному, лише викликав його побічний ефект. З іншого боку, async, який повертає відкладене, як я вже говорив, повертає корисне значення при виконанні його блоку - об'єкта, який обертає ваші дані, тому ви можете використовувати його в основному для його результату, але можливо і для його побічного ефекту. Примітка: ви можете зняти відкладений і отримати його значення за допомогою функції await, яка блокує виконання ваших операторів, поки значення не повернеться або не буде викинуто виняток!

  3. обидва програми для створення програм (запуск та асинхронізація) скасовуються.

  4. що-небудь більше?: так з запуском, якщо виняток буде кинуто в його блок, програма автоматично скасовується і винятки доставляються. З іншого боку, якщо це трапляється з асинхронізацією, виняток не поширюється далі, а його слід спіймати / обробляти у поверненому об'єкті Відкладено

  5. детальніше про супроводи https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1


1
Дякую за цей коментар У ньому зібрані всі точки нитки. Додам, що не всі запуски скасовуються, наприклад, Atomic не може бути скасований ніколи.
p2lem8dev

4

запуск повертає роботу

async повертає результат (відкладене завдання)

запуск із приєднанням використовується для очікування, коли робота закінчиться. Він просто призупиняє виклик програми, що викликає join (), залишаючи поточний потік вільним для виконання іншої роботи (наприклад, виконання іншої програми) тим часом.

async використовується для обчислення деяких результатів. Він створює підпрограму і повертає її майбутній результат як реалізацію відкладеного. Запущена коренева програма скасовується, коли отримане відкладене скасовується.

Розглянемо метод асинхронізації, який повертає значення рядка. Якщо метод асинхронізації використовується без очікування, він поверне рядок "Відкладений", але якщо використовується "очікування", ви отримаєте рядок як результат

Ключова різниця між асинхронізацією та запуском. Відкладене повертає певне значення типу T після завершення виконання програми Coroutine, тоді як робота не виконує.


0

Async vs Launch Async vs Launch Diff Image

запуск / асинхронізація не має результату

  • Використовувати, коли не потрібен результат,
  • Не блокуйте код, де викликається,
  • Бігайте паралельно

асинхронізація результату

  • Коли вам потрібно дочекатися результату і можете працювати паралельно для ефективності
  • Блокуйте код, куди викликається
  • бігати паралельно
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.