Kotlin programlarında başlatma / birleştirme ve zaman uyumsuz / beklemenin arasındaki fark nedir


Yanıtlar:


232
  • launchateş ve coroutine unutmak için kullanılır . Yeni bir konu açmak gibidir. İçindeki kod launchistisna ile sonlanırsa , bir iş parçacığında yakalanmamış istisna gibi davranılır - genellikle arka uç JVM uygulamalarında stderr'a yazdırılır ve Android uygulamalarını kilitler. joinbaşlatılan programın tamamlanmasını beklemek için kullanılır ve istisnasını yaymaz. Ancak, çökmüş bir çocuk coutini, ebeveynini de buna karşılık gelen istisna ile iptal eder.

  • asyncbazı sonuçları hesaplayan bir programın başlatılması için kullanılır . Sonuç bir örneği ile temsil edilir Deferredve üzerinde kullanmanız gerekirawait . asyncKodun içinde yakalanmamış bir istisna , sonuçta saklanır Deferredve başka bir yere teslim edilmez, işlenmedikçe sessizce düşecektir. Zaman uyumsuzluğuyla başladığınız koroutini unutmamalısınız .


1
Android ağ çağrıları için doğru Coroutine oluşturucu nedir?
Faraaz

Doğru ortak program oluşturucu ne yapmaya çalıştığınıza bağlıdır
Roman Elizarov

9
"Zaman uyumsuzlukla başladığınız koroutini UNUTMAMALISINIZ" konusunu açıklayabilir misiniz? Örneğin beklenmeyecek bir şey var mı?
Luis

2
"Eşzamansız kod içinde yakalanmamış bir istisna, oluşan Ertelenmiş öğenin içinde saklanır ve başka bir yere teslim edilmez, işlenmedikçe sessizce düşecektir."
Roman Elizarov

9
Asenkron sonucunu unutursanız bitirir ve çöp toplanır. Ancak, kodunuzdaki bir hata nedeniyle çökerse, bunu asla öğrenemezsiniz. Bu yüzden.
Roman Elizarov

77

Bu kılavuzu https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md faydalı buluyorum . Önemli parçaları teklif edeceğim

🦄 eşyordam

Esasen, koroutinler hafif ipliklerdir.

Böylece koroutini, ipliği çok verimli bir şekilde yöneten bir şey olarak düşünebilirsiniz.

🐤 başlat

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
}

Böylece launchbir arka plan iş parçacığı başlar, bir şey yapar ve hemen bir jeton olarak döner Job. Arayabilirsinjoin bu konuda Jobbu kadar engellemek için launchiplik tamamlanıncaya

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
}

🦆 zaman uyumsuz

Kavramsal olarak, asenkron tıpkı lansman gibidir. Diğer tüm koroutinlerle aynı anda çalışan hafif bir iplik olan ayrı bir koroutin başlatır. Fark, lansmanın bir İş döndürdüğü ve sonuçta herhangi bir değer taşımadığı, async ise Ertelenmiş - daha sonra bir sonuç verme vaadini temsil eden hafif, bloke olmayan bir gelecek döndürür.

Böylece asyncbir arka plan iş parçacığı başlar, bir şey yapar ve hemen bir jeton olarak döner 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")
}

Son sonucunu almak için ertelenmiş bir değerde .await () kullanabilirsiniz, ancak Ertelenmiş de bir İştir, bu nedenle gerekirse iptal edebilirsiniz.

Yani Deferredaslında bir olduğunu Job. Görmek Https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html

interface Deferred<out T> : Job (source)

🦋 uyumsuz varsayılan olarak istekli

CoroutineStart.LAZY değerine sahip isteğe bağlı bir başlangıç ​​parametresi kullanarak zaman uyumsuzluk için tembellik seçeneği vardır. Koroutine ancak sonucu bazılarının beklemesi gerektiğinde veya bir başlatma fonksiyonu çağrıldığında başlar.


11

launch ve async yeni ortak programlar başlatmak için kullanılır. Ancak, onları farklı bir şekilde yürütürler.

Farkı kolayca anlamanıza yardımcı olacak çok temel bir örnek göstermek istiyorum

  1. başlatmak
    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;
    }
}

Bu örnekte, kodum btnCountdüğmeyi tıkladığınızda 3 veri indiriyor ve pgBartüm indirme tamamlanana kadar ilerleme çubuğunu gösteriyor . 3 Var suspendfonksiyonlar downloadTask1(), downloadTask2()ve downloadTask3()veri indirir. Benzetmek için delay()bu işlevlerde kullandım. Bu işlevler bekler 5 seconds, 8 secondsve5 seconds sırasıyla.

launchBu askıya alma işlevlerini başlatmak için kullandığımız için, launchbunları sırayla (tek tek) yürütür . Ki bu araçlar downloadTask2()sonra başlayacak downloadTask1()tamamlanmış olur ve downloadTask3()sonra başlayacakdownloadTask2() .

Çıkış ekran gibi Toast, toplam uygulama süresi 3 yükle yol açacak tamamlamak için 5 saniye + 8 saniye + 5 saniye = 18 saniye ilelaunch

Lansman Örneği

  1. zaman uyumsuz

Gördüğümüz gibi o launchyürütülmesine yapan sequentially3 görevler için. Tüm görevleri tamamlamanın zamanı geldi 18 seconds.

Bu görevler bağımsızsa ve başka bir görevin hesaplama sonucuna ihtiyaç duymazlarsa, onları çalıştırabiliriz concurrently. Aynı anda başlayıp arka planda eşzamanlı olarak çalışırlardı. Bu ile yapılabilir async.

asyncDeffered<T>tür örneğini döndürür ; burada Taskıya alma işlevimizin döndürdüğü veri türüdür. Örneğin,

  • downloadTask1()dönecekti Deferred<String>dize olarak fonksiyonun dönüş türüdür
  • downloadTask2()dönecekti Deferred<Int>Int olarak fonksiyonun dönüş türüdür
  • downloadTask3()dönecekti Deferred<Float>Float olarak fonksiyonun dönüş türüdür

Türden döndürülen değeri almak için asynctürden dönüş nesnesini kullanabiliriz . Bu çağrı ile yapılabilir . Örneğin aşağıdaki kodu kontrol edinDeferred<T>Tawait()

        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
        }

Bu şekilde, 3 görevin hepsini aynı anda başlattık. Yani, toplam yürütme sürem sadece 3 görevden en büyüğü 8 secondsolduğu zaman olacak downloadTask2(). Bunu aşağıdaki ekran görüntüsünde görebilirsiniz.Toast message

örnek beklemek


1
Bu söz için teşekkür ederiz launchiçindir sıralı , eğlenceler sırasında asynciçin eşzamanlı
Akbolat SSS

Tüm görevler için başlat'ı ve her biri için zaman uyumsuzluğu kullandınız. Belki daha hızlıdır, çünkü her biri başka bir programda başlatıldı ve birini beklemiyor mu? Bu yanlış bir karşılaştırma. Genellikle performans aynıdır. En önemli farklardan biri, lansmanın sahibini bölen asenkron yerine her zaman yeni bir program başlatmasıdır. Bir başka faktör de, zaman uyumsuz görevlerden birinin bir nedenle başarısız olması durumunda, ana ortak programın da başarısız olacağıdır. Bu yüzden async, lansman kadar popüler değil.
p2lem8dev

1
Bu cevap doğru değil, async'i başlatma yerine doğrudan askıya alma işlevleriyle karşılaştırıyor. Doğrudan askıya alma işlevini çağırmak yerine, launch (Dispatchers.IO) {downloadTask1 ()} öğesini çağırırsanız, her ikisinin de sıralı olarak değil , aynı anda yürütüldüğünü görürsünüz, ancak çıktıları alamazsınız. sıralı değil. Ayrıca deferred.await () öğesini birleştirmezseniz ve deferred.await () öğesini ayrı olarak çağırmazsanız, zaman uyumsuzluğun sıralı olduğunu görürsünüz.
Trakya

2
-1 bu sadece yanlış. Her ikisi de launchve asyncyeni coroutines başlayacak. Hiçbir çocuğu olmayan tek bir koroutini 3 çocuğu olan tek bir koroutinin karşılaştırması yapıyorsunuz. Her bir asyncçağrıyı launchdeğiştirebilirsiniz ve eşzamanlılık konusunda kesinlikle hiçbir şey değişmez.
Moira

Bu cevaptaki aşırı gürültü, rutin konunun dışında bir karmaşıklık katıyor.
truthadjustr

6
  1. her iki birlikte oluşturucu, lansman ve eşzamansız olarak temelde CoroutineScope tipi alıcıya sahip lambdaslardır, bu da iç bloklarının bir askıya alma işlevi olarak derlendiği anlamına gelir, böylece her ikisi de eşzamansız modda çalışır ve her ikisi de bloklarını sırayla yürütür.

  2. Başlatma ve zaman uyumsuzluk arasındaki fark, iki farklı olasılık sağlamalarıdır. Başlatma oluşturucusu bir İş döndürür, ancak zaman uyumsuz işlev Ertelenmiş bir nesne döndürür. Herhangi bir döndürülen değeri beklemediğiniz bir bloğu yürütmek için başlat'ı kullanabilirsiniz, yani bir veritabanına yazmak veya bir dosyayı kaydetmek veya temel olarak yan etkisi için çağrılan bir şeyi işlemek. Öte yandan, daha önce de belirttiğim gibi bir Ertelenmiş döndüren asenkron, bloğunun yürütülmesinden, verilerinizi saran bir nesne olan yararlı bir değer döndürür, böylece esas olarak sonucu için ama muhtemelen yan etkisi için de kullanabilirsiniz. Not: ertelenebilir ve değerini bir değer döndürülünceye veya bir istisna atılana kadar ifadelerinizin yürütülmesini engelleyecek olan işlev bekliyor işlevini kullanarak alabilirsiniz!

  3. her iki program oluşturucu da (başlatma ve zaman uyumsuz) iptal edilebilir.

  4. başka bir şey var mı ?: bloğuna bir istisna atılırsa, koroutin otomatik olarak iptal edilir ve istisnalar verilir. Öte yandan, bu zaman uyumsuzluk ile gerçekleşirse, istisna daha fazla yayılmaz ve döndürülen Ertelenmiş nesne içinde yakalanmalı / ele alınmalıdır.

  5. coroutines hakkında daha fazla bilgi https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1


1
Bu yorum için teşekkürler. İpliğin tüm noktalarını topladı. Tüm lansmanların iptal edilmediğini eklerim, örneğin Atomic hiçbir zaman iptal edilemez.
p2lem8dev

4

başlatmak bir iş döndürür

zaman uyumsuz bir sonuç döndürür (ertelenmiş iş)

join ile başlatmak, iş bitene kadar beklemek için kullanılır. bu arada, mevcut iş parçacığını (başka bir coroutine yürütmek gibi) diğer işleri yapmak için serbest bırakarak coroutine call join () öğesini askıya alır.

zaman uyumsuz bazı sonuçları hesaplamak için kullanılır. Bir koroutin oluşturur ve gelecekteki sonucunu Ertelenmiş bir uygulama olarak döndürür. Ortaya çıkan erteleme, ertelenen erteleme iptal edildiğinde iptal edilir.

Bir dize değeri döndüren bir zaman uyumsuz yöntem düşünün. Async yöntemi beklemeden kullanılırsa, Ertelenmiş bir dize döndürür, ancak beklenirse sonuç olarak bir dize alırsınız

Zaman uyumsuzluk ve başlatma arasındaki temel fark. Ertelenmiş, Coroutine'nizin yürütülmesi bittikten sonra belirli bir T türü değeri döndürür, ancak Job bunu yapmaz.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.