Kotlin Coroutine'de askıya alma işlevi ne anlama geliyor?


118

Kotlin Coroutine okuyorum ve bunun suspendişleve dayalı olduğunu biliyorum . Ama ne anlama suspendgeliyor?

Coroutine veya fonksiyon askıya mı alındı?

Gönderen https://kotlinlang.org/docs/reference/coroutines.html

Temel olarak, eş değerler, bir iş parçacığını engellemeden askıya alınabilen hesaplamalardır

İnsanların sık sık "işlevi askıya al" dediklerini duydum. Ama bence görevin bitmesini beklediği için askıya alınan coroutine mi? "askıya alma" genellikle "işlemi durdur" anlamına gelir, bu durumda koroutin boştadır.

🤔 Koroutin askıya alındı ​​mı demeliyiz?

Hangi korutin askıya alınır?

Gönderen https://kotlinlang.org/docs/reference/coroutines.html

Analojiye devam etmek için, await (), bir hesaplama yapılıncaya kadar bir eşdizimi askıya alan ve sonucunu döndüren bir askıya alma işlevi olabilir (bu nedenle bir eşzamansız {} bloğundan da çağrılabilir):

async { // Here I call it the outer async coroutine
    ...
    // Here I call computation the inner coroutine
    val result = computation.await()
    ...
}

🤔 "Bir hesaplama yapılana kadar bir koroutini askıya alır" diyor, ancak koroutin hafif bir iş parçacığı gibidir. Öyleyse, koroutin askıya alınırsa, hesaplama nasıl yapılabilir?

Biz bkz awaitüzerinde denir computationbu olabilir böylece, asynco döner Deferredbaşka eşyordam başlatabileceği konusunda araçlar,

fun computation(): Deferred<Boolean> {
    return async {
        true
    }
}

🤔 Alıntı , bir koroutini askıya aldığını söylüyor . Bu ifadeyle suspenddış asynceşyordam veya suspendcomputationeşyordam?

Does suspendortalama o dış ederken asynceşyordam (bekliyor awaitiç için) computationbitirmek için eşyordamın, bu (dış asynceşyordam) iplik havuzuna Idles (dolayısıyla adı askıya) ve döner iplik ve ne zaman çocuk computationeşyordam bitirir, bu (dış asynceşyordam ) uyanır, havuzdan başka bir konu alır ve devam eder?

Konu başlığından bahsetmemin nedeni https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html

İş parçacığı, koroutin beklerken havuza döndürülür ve bekleme tamamlandığında, koroutin havuzdaki boş bir iş parçacığı üzerinde devam eder.

Yanıtlar:


114

Askıya alma fonksiyonları her şeyin merkezinde yer alır. Askıya alma işlevi, daha sonra duraklatılabilen ve devam ettirilebilen bir işlevdir. Uzun süren bir işlemi yürütebilir ve engellemeden tamamlanmasını bekleyebilirler.

Bir askıya alma işlevinin sözdizimi, suspendanahtar kelimenin eklenmesi dışında normal bir işlevinkine benzer . Bir parametre alabilir ve bir dönüş tipine sahip olabilir. Ancak, askıya alma işlevleri yalnızca başka bir askıya alma işlevi tarafından veya bir koroutin içinde çağrılabilir.

suspend fun backgroundTask(param: Int): Int {
     // long running operation
}

Kapanışta, askıya alma işlevleri, derleyici tarafından, suspend anahtar sözcüğü olmadan başka bir işleve dönüştürülür ve bu, bir ek parametre türü alır Continuation<T>. Örneğin yukarıdaki işlev, derleyici tarafından buna dönüştürülecektir:

fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
   // long running operation
}

Continuation<T> işlev askıya alındığında bir hata meydana gelmişse, bir dönüş değeriyle veya bir istisna ile birlikte korutini devam ettirmek için çağrılan iki işlevi içeren bir arabirimdir.

interface Continuation<in T> {
   val context: CoroutineContext
   fun resume(value: T)
   fun resumeWithException(exception: Throwable)
}

5
Başka bir gizem ortaya çıktı! Harika!
WindRider

16
Acaba bu işlev aslında nasıl duraklatıldı? Her zaman suspend funbunun duraklatılabileceğini söylüyorlar ama tam olarak nasıl?
WindRider

2
@WindRider Bu sadece mevcut iş parçacığının başka bir coroutine yürütmeye başladığı ve buna daha sonra geri döneceği anlamına gelir.
Joffrey

2
"Gizemli" mekanizmayı buldum. Tools> Kotlin> Bytecode> Decompile btn yardımı ile kolayca açılabilir. Devam yoluyla, sözde "askıya alma noktası" nın nasıl uygulandığını gösterir. Herkes kendinize bakabilir.
WindRider

4
@buzaa İşte Roman Elizarov'un 2017'den bayt kodu seviyesinde açıkladığı bir konuşma .
Marko Topolnik

30

Bir koroutini askıya almanın tam olarak ne anlama geldiğini anlamak için şu kodu gözden geçirmenizi öneririm:

import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

var continuation: Continuation<Int>? = null

fun main() = runBlocking {
    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
        }
    }
}

UnconfinedEşyordam memuru sihirli ortadan kaldırır eşyordam sevkiyat ve bize çıplak değiş tokuş eden kavramlar doğrudan odaklanmasını sağlar.

launchBloğun içindeki kod , launchçağrının bir parçası olarak mevcut iş parçacığı üzerinde hemen yürütülmeye başlar . Olanlar aşağıdaki gibidir:

  1. Değerlendirmek val a = a()
  2. Bu zincirlere b()ulaşıyor suspendCoroutine.
  3. İşlev b(), iletilen bloğu yürütür suspendCoroutineve ardından özel bir COROUTINE_SUSPENDEDdeğer döndürür . Bu değer, Kotlin programlama modeli aracılığıyla gözlemlenemez, ancak derlenen Java yönteminin yaptığı budur.
  4. a()Bu dönüş değerini gören işlev de onu döndürür.
  5. launchBlok aynı işi yapar ve kontrol artık sonra gelen satıra döner launchçağırma:10.downTo(0)...

Bu noktada, launchbloğun içindeki kod ve fun mainkodunuz aynı anda çalışıyormuş gibi aynı etkiye sahip olduğunuzu unutmayın . Sadece tüm bunlar tek bir yerel iş parçacığı üzerinde gerçekleşiyor, bu yüzden launchblok "askıya alındı".

Şimdi, içeride forEachkod döngü program okur continuationo b()fonksiyon yazdım ve resumesdeğeri ile 10. resume()öyle bir şekilde uygulanır ki, suspendCoroutineçağrı geçtiğiniz değerle geri dönmüş gibi olur . Böylece kendinizi aniden yürütmenin ortasında bulursunuz b(). Geçtiğiniz değer resume()atanır ive karşılaştırılır 0. Sıfır değilse, while (true)döngü içeride devam eder b(), tekrar ulaşır suspendCoroutine, bu noktada resume()çağrınız geri döner ve şimdi başka bir döngü adımından geçersiniz forEach(). Bu, siz devam edene kadar devam eder 0, ardından printlnifade çalışır ve program tamamlanır.

Yukarıdaki analiz size, "bir eşdizimi askıya almanın", denetimi en içteki çağrıyalaunch (veya daha genel olarak, koroutin oluşturucuya ) geri döndürmek anlamına geldiğine dair önemli bir sezgi vermelidir . Bir koroutin devam ettirildikten sonra tekrar askıya alınırsa, resume()çağrı sona erer ve kontrol, arayana geri döner resume().

Bir coroutine dağıtıcısının varlığı, çoğu kodunuzu hemen başka bir iş parçacığına gönderdiği için bu mantığı daha az açık hale getirir. Bu durumda, yukarıdaki hikaye diğer iş parçacığında gerçekleşir ve koroutin dağıtıcısı da continuationnesneyi yönetir, böylece dönüş değeri mevcut olduğunda onu devam ettirebilir.


21

Her şeyden önce, bu IMO'yu anlamak için en iyi kaynak, Roman Elizarov'un "Coroutines'e Derinlemesine Dalış" konuşmasıdır .

Coroutine veya fonksiyon askıya mı alındı?

Bir askıya Arama ing işlevini askıya s a başka eşyordam yürütülmesi başlayabilirsiniz akım iplik anlamına eşyordam. Dolayısıyla, koroutinin işlevden ziyade askıya alındığı söylenir.

Aslında, askıya alma işlevi olan çağrı sitelerine bu nedenle "askıya alma noktaları" denir.

Hangi korutin askıya alınır?

Kodunuza bakalım ve ne olduğunu inceleyelim:

// 1. this call starts a new coroutine (let's call it C1).
//    If there were code after it, it would be executed concurrently with
//    the body of this async
async {
    ...
    // 2. this is a regular function call
    val deferred = computation()
    // 4. because await() is suspendING, it suspends coroutine C1.
    //    This means that if we had a single thread in our dispatcher, 
    //    it would now be free to go execute C2
    // 7. once C2 completes, C1 is resumed with the result `true` of C2's async
    val result = deferred.await() 
    ...
    // 8. C1 can now keep going in the current thread until it gets 
    //    suspended again (or not)
}

fun computation(): Deferred<Boolean> {
    // 3. this async call starts a second coroutine (C2). Depending on the 
    //    dispatcher you're using, you may have one or more threads.
    // 3.a. If you have multiple threads, the block of this async could be
    //      executed in parallel of C1 in another thread. The control flow 
    //      of the current thread returns to the caller of computation().
    // 3.b. If you have only one thread, the block is sort of "queued" but 
    //      not executed right away, and the control flow returns to the 
    //      caller of computation(). (unless a special dispatcher or 
    //      coroutine start argument is used, but let's keep it simple).
    //    In both cases, we say that this block executes "concurrently"
    //    with C1.
    return async {
        // 5. this may now be executed
        true
        // 6. C2 is now completed, so the thread can go back to executing 
        //    another coroutine (e.g. C1 here)
    }
}

Dış asyncbir eşyordam başlar. O aradığında computation(), iç asyncikinci eşyordam başlar. Daha sonra, çağrı await()askıya yürütülmesi dış async yürütülmesi kadar eşyordamın, async sitesindeki eşyordamın bitti.

Bunu tek bir iş parçacığı ile bile görebilirsiniz: iş parçacığı dışın asyncbaşlangıcını yürütecek , sonra çağıracak computation()ve iç kısma ulaşacaktır async. Bu noktada, iç asenkronun gövdesi atlanır ve iş parçacığı, asynculaşana kadar dışını yürütmeye devam eder await(). await()bir "askıya alma noktası", çünkü awaitbir askıya alma işlevi. Bu, dış korutinin askıya alındığı ve böylece iş parçacığının içteki olanı yürütmeye başladığı anlamına gelir. Tamamlandığında, dışın sonunu yürütmek için geri gelir async.

Askıya alma, dış zaman uyumsuz coroutininin iç hesaplama eşdiziminin bitmesini beklerken (beklerken), (dış zaman uyumsuz kordin) boşta kaldığı (dolayısıyla adı askıya alındığı için) ve iş parçacığı havuzuna döndürdüğü ve alt hesaplama eşdiziminin tamamlandığı anlamına gelir. , o (dış asenkron korutin) uyanır, havuzdan başka bir iş parçacığı alır ve devam eder?

Evet, kesinlikle.

Bunun gerçekte başarılma yolu, her askıya alma işlevini bir durum makinesine dönüştürmektir; burada her "durum", bu askıya alma işlevi içindeki bir askıya alma noktasına karşılık gelir. Başlık altında, işlev hangi askıya alma noktasından yürütülmeye başlaması gerektiği bilgisiyle birden çok kez çağrılabilir (bununla ilgili daha fazla bilgi için bağladığım videoyu gerçekten izlemelisiniz).


3
Harika cevap, konu korutinler olduğunda bu tür gerçekten temel açıklamayı özlüyorum.
bernardo.g

Neden bu başka bir dilde uygulanmıyor? Yoksa bir şey mi kaçırıyorum? Bu çözümü çok uzun zamandır düşünüyorum, Kotlin'in buna sahip olmasına sevindim, ancak TS veya
Rust'un

@PEZO iyi coroutines uzun zamandır etrafta. Kotlin onları icat etmedi, ancak sözdizimi ve kütüphane onları parlatıyor. Go'nun goroutinleri, JavaScript ve TypeScript'in vaatleri vardır. Tek fark, bunları kullanmak için söz diziminin ayrıntılarındadır. JS'nin asyncişlevlerinin bu şekilde işaretlenmesini ve yine de bir Söz vermesini oldukça can sıkıcı / rahatsız edici buluyorum .
Joffrey

Üzgünüm, yorumum net değildi. Askıya alma anahtar kelimesine atıfta bulunuyorum. Async ile aynı şey değildir.
PEZO

Roman'ın videosunu gösterdiğin için teşekkürler. Saf altın.
Denounce'IN

8

Anlamanın en iyi yolunun anahtar kelime ve özellik suspendarasında bir benzetme yapmak olduğunu buldum .thiscoroutineContext

Kotlin fonksiyonları yerel veya global olarak tanımlanabilir. Yerel işlevler sihirli bir şekilde thisanahtar kelimeye erişebilirken , küresel değil.

Kotlin işlevleri olarak bildirilebilir suspendveya engellenebilir. suspendişlevler sihirli bir şekilde özelliğe erişebilirken, coroutineContextişlevler engellenmez.

Konu şudur: coroutineContextmülkiyet Kotlin stdlib'de "normal" bir özellik olarak bildirilir, ancak bu bildirim yalnızca dokümantasyon / gezinme amaçlı bir saplamadır. Aslında coroutineContext, derleyicinin altında bu özelliğin dil anahtar kelimelerinin farkında olması gibi sihir anlamına gelen yerleşik iç özelliktir .

Ne thiskelime yerel işlevleri için yaptığı iştir coroutineContextmülkiyet için yaptığı suspendfonksiyonları: o yürütme mevcut bağlamda erişim sağlar.

Bu nedenle, özelliğe suspenderişim elde etmeniz gerekir coroutineContext- şu anda yürütülen coroutine bağlamının örneği


5

Size devam kavramının basit bir örneğini vermek istedim. Bir askıya alma işlevinin yaptığı budur, donabilir / askıya alabilir ve sonra devam eder / devam eder. Koroutini iş parçacıkları ve Semafor açısından düşünmeyi bırakın. Devam etme ve hatta geri arama kancaları açısından düşünün.

Açık olmak gerekirse, bir koroutin bir suspendişlev kullanılarak duraklatılabilir . bunu araştıralım:

Android'de bunu örneğin yapabiliriz:

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")
                }
            }
        }

Yukarıdaki kod aşağıdakileri yazdırır:

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

bunun böyle çalıştığını hayal edin:

görüntü açıklamasını buraya girin

Böylece başlattığınız mevcut işlev durmaz, devam ederken sadece bir koroutin askıya alınır. Bir askıya alma işlevi çalıştırılarak iş parçacığı duraklatılmaz.

Sanırım bu site size açık bir şekilde yardımcı olabilir ve benim referansım.

Güzel bir şey yapalım ve askıya alma işlevimizi bir yinelemenin ortasında donduralım. Daha sonra devam edeceğizonResume

Çağrılan bir değişkeni continuationsaklayın ve biz onu bizim için coroutines devam nesnesi ile yükleyelim:

var continuation: CancellableContinuation<String>? = null

suspend fun freezeHere() = suspendCancellableCoroutine<String> {
            continuation = it
        }

 fun unFreeze() {
            continuation?.resume("im resuming") {}
        }

Şimdi askıya alınmış fonksiyonumuza dönelim ve yinelemenin ortasında donmasını sağlayalım:

 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
                }
            }
        }
    }

Sonra onResume gibi başka bir yerde (örneğin):

override fun onResume() {
        super.onResume()
        unFreeze()
    }

Ve döngü devam edecek. Bir askıya alma işlevini herhangi bir noktada dondurabileceğimizi ve bir süre geçtikten sonra devam ettirebileceğimizi bilmek çok güzel. Kanallara da bakabilirsiniz


4

Zaten pek çok iyi cevap olduğu için, diğerleri için daha basit bir örnek vermek istiyorum.

runBlocking kullanım durumu:

  • myMethod () suspendişlevdir
  • runBlocking { }bloke edici şekilde bir Coroutine başlatır. Bu, normal iş parçacıklarını Threadsınıfla engelleme ve belirli olaylardan sonra engellenen konuları bildirme şeklimize benzer .
  • runBlocking { }etmez blok eşyordamın kadar iplik yürütme akım (vücut arasında {}) tamamlanmış olur

     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);
        }
    }

Bu çıktılar:

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

kullanım durumunu başlat :

  • launch { } eşzamanlı olarak bir eşgüdüm başlatır.
  • Bu, launch'i belirttiğimizde, bir koroutin workeriş parçacığı üzerinde yürütmeye başlayacağı anlamına gelir .
  • İş workerparçacığı ve dış iş parçacığı (içinden çağırdığımız launch { }) her ikisi de aynı anda çalışır. Dahili olarak, JVM Preemptive Threading gerçekleştirebilir
  • Paralel olarak çalışmak için birden fazla göreve ihtiyaç duyduğumuzda, bunu kullanabiliriz. Orada scopeseşyordamın ömrünü belirtmek hangi. Biz belirtirseniz GlobalScope, eşyordam uygulama süresi bitene kadar çalışacak.

    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);
            }
        }
    }

Bu Çıktılar:

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

eşzamansız ve kullanım durumunu bekleyin :

  • Yapacak birden fazla görevimiz olduğunda ve bunlar başkalarının tamamlanmasına bağlı asyncve awaityardımcı olabilir.
  • Örneğin, aşağıdaki kodda, 2myMethod () ve myMethod2 () askıya alma işlevleri vardır . myMethod2()tam anlamıyla tamamlanabilmesi sonra infaz almalısınız myMethod() OR myMethod2() sonuçlarına bağlıdır myMethod(), biz kullanabilirsiniz asyncveawait
  • asyncbenzer şekilde bir koroutin başlatır launch. Ancak, paralel olarak başka bir coroutine başlamadan önce bir coroutine beklemek için bir yol sağlar.
  • Bu yol await(). asyncbir örneğini döndürür Deffered<T>. Tolacağını Unitvarsayılan için. Herhangi beklemek gerektiğinde async'ın tamamlanmasıyla, biz görüşmeye ihtiyaç .await()üzerine Deffered<T>bunun örneği async. Aşağıdaki örnekte innerAsync.await()olduğu gibi, infazın innerAsynctamamlanana kadar askıya alınacağını ima eden çağrıda bulunduk . Aynı şeyi çıktıda da gözlemleyebiliriz. innerAsyncHangi çağrıları, ilk tamamlanmış olur myMethod(). Ve sonra async innerAsync2başlar, hangi çağırırmyMethod2()

    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);
            }
        }
    }

Bu çıktılar:

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
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.