Apa arti fungsi suspend di Kotlin Coroutine

118

Saya membaca Kotlin Coroutine dan tahu bahwa ini didasarkan pada suspendfungsi. Tapi apa suspendartinya?

Coroutine atau fungsi ditangguhkan?

Dari https://kotlinlang.org/docs/reference/coroutines.html

Pada dasarnya, coroutine adalah komputasi yang dapat ditangguhkan tanpa memblokir utas

Saya mendengar orang sering mengatakan "fungsi tunda". Tapi menurut saya coroutine-nya yang di-suspend karena menunggu fungsinya selesai? "suspend" biasanya berarti "hentikan operasi", dalam hal ini coroutine dalam keadaan idle.

🤔 Haruskah kita mengatakan coroutine ditangguhkan?

Coroutine mana yang ditangguhkan?

Dari https://kotlinlang.org/docs/reference/coroutines.html

Untuk melanjutkan analoginya, await () bisa menjadi fungsi penangguhan (karenanya juga bisa dipanggil dari dalam blok {} async) yang menangguhkan coroutine sampai beberapa komputasi selesai dan mengembalikan hasilnya:

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

🤔 Dikatakan "yang menangguhkan coroutine sampai beberapa komputasi selesai", tetapi coroutine seperti thread yang ringan. Jadi, jika coroutine di-suspend, bagaimana komputasi bisa dilakukan?

Kami melihat awaitdipanggil computation, jadi mungkin asyncitu kembali Deferred, yang berarti dapat memulai coroutine lain

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

🤔 Kutipan mengatakan bahwa menghentikan coroutine . Apakah itu berarti coroutine suspendluar async, atau coroutine suspenddalam computation?

Apakah suspendberarti bahwa sementara asynccoroutine luar sedang menunggu ( await) untuk computationcoroutine bagian dalam selesai, itu ( asynccoroutine luar ) menganggur (karena itu nama menangguhkan) dan mengembalikan utas ke kumpulan utas, dan ketika computationcoroutine turunan selesai, itu ( asynccoroutine luar ) bangun, mengambil utas lain dari kolam dan melanjutkan?

Alasan saya menyebutkan utas ini adalah karena https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html

Utas dikembalikan ke kumpulan saat coroutine menunggu, dan saat penantian selesai, coroutine dilanjutkan pada utas gratis di kumpulan

onmyway133
sumber

Jawaban:

113

Fungsi penangguhan adalah pusat dari semua coroutine. Fungsi penangguhan hanyalah sebuah fungsi yang dapat dijeda dan dilanjutkan di lain waktu. Mereka dapat menjalankan operasi yang berjalan lama dan menunggu hingga selesai tanpa memblokir.

Sintaks fungsi penangguhan mirip dengan fungsi biasa kecuali untuk penambahan suspendkata kunci. Itu bisa mengambil parameter dan memiliki tipe kembali. Namun, fungsi penangguhan hanya dapat dijalankan oleh fungsi penangguhan lain atau dalam coroutine.

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

Di bawah tenda, fungsi penangguhan diubah oleh kompilator ke fungsi lain tanpa kata kunci penangguhan, yang mengambil parameter tambahan tipe Continuation<T>. Fungsi di atas misalnya, akan diubah oleh compiler menjadi ini:

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

Continuation<T> adalah antarmuka yang berisi dua fungsi yang dipanggil untuk melanjutkan coroutine dengan nilai kembali atau dengan pengecualian jika terjadi kesalahan saat fungsi ditangguhkan.

interface Continuation<in T> {
   val context: CoroutineContext
   fun resume(value: T)
   fun resumeWithException(exception: Throwable)
}
Sofien Rahmouni
sumber
4
Misteri lain terungkap! Bagus!
WindRider
16
Saya bertanya-tanya bagaimana sebenarnya fungsi ini dijeda? Mereka selalu bilang suspend funbisa dijeda tapi bagaimana tepatnya?
WindRider
2
@WindRider Ini hanya berarti bahwa utas saat ini mulai menjalankan beberapa coroutine lain, dan akan kembali ke yang ini nanti.
Joffrey
2
Saya telah menemukan mekanisme "misterius". Ini dapat dengan mudah diungkapkan dengan bantuan Tools> Kotlin> Bytecode> Decompile btn. Ini menunjukkan bagaimana yang disebut "titik suspensi" diimplementasikan - melalui Kelanjutan dan seterusnya. Siapapun bisa memeriksanya sendiri.
WindRider
4
@buzaa Berikut adalah pembicaraan dari tahun 2017 oleh Roman Elizarov yang menjelaskannya pada tingkat bytecode.
Marko Topolnik
30

Untuk memahami apa sebenarnya artinya menangguhkan coroutine, saya sarankan Anda membaca kode ini:

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

Operator Unconfinedcoroutine menghilangkan keajaiban pengiriman coroutine dan memungkinkan kita untuk fokus langsung pada coroutine kosong.

Kode di dalam launchblok mulai dijalankan langsung di utas saat ini, sebagai bagian dari launchpanggilan. Yang terjadi adalah sebagai berikut:

  1. Evaluasi val a = a()
  2. Rantai ini untuk b(), mencapai suspendCoroutine.
  3. Fungsi b()mengeksekusi blok yang diteruskan suspendCoroutinedan kemudian mengembalikan COROUTINE_SUSPENDEDnilai khusus . Nilai ini tidak dapat diamati melalui model pemrograman Kotlin, tetapi itulah yang dilakukan metode Java yang dikompilasi.
  4. Fungsi a(), melihat nilai kembalian ini, juga mengembalikannya.
  5. The launchblok melakukan hal yang sama dan kontrol sekarang kembali ke jalur setelah launchdoa:10.downTo(0)...

Perhatikan bahwa, pada titik ini, Anda memiliki efek yang sama seperti jika kode di dalam launchblok dan fun mainkode Anda dijalankan secara bersamaan. Kebetulan semua ini terjadi pada satu utas asli sehingga launchblok "ditangguhkan".

Sekarang, di dalam forEachkode perulangan, program membaca continuationbahwa b()fungsi tersebut menulis dan resumesdengan nilai 10. resume()diimplementasikan sedemikian rupa sehingga seakan-akan suspendCoroutinepanggilan itu dikembalikan dengan nilai yang Anda berikan. Jadi, Anda tiba-tiba menemukan diri Anda berada di tengah-tengah eksekusi b(). Nilai yang Anda resume()berikan akan ditetapkan idan diperiksa 0. Jika bukan nol, while (true)loop berlanjut ke dalam b(), lagi-lagi mencapai suspendCoroutine, di mana resume()panggilan Anda kembali, dan sekarang Anda melalui langkah perulangan lainnya forEach(). Ini berlangsung sampai akhirnya Anda melanjutkan 0, kemudian printlnpernyataan berjalan dan program selesai.

Analisis di atas akan memberi Anda intuisi penting bahwa "menangguhkan coroutine" berarti mengembalikan kontrol ke launchpemanggilan terdalam (atau, lebih umum, pembuat coroutine ). Jika coroutine dihentikan lagi setelah dilanjutkan, resume()panggilan berakhir dan kontrol kembali ke pemanggil resume().

Kehadiran petugas operator coroutine membuat alasan ini kurang jelas karena kebanyakan dari mereka segera mengirimkan kode Anda ke utas lain. Dalam hal ini, cerita di atas terjadi di utas lain itu, dan dispatcher coroutine juga mengelola continuationobjek tersebut sehingga dapat melanjutkannya ketika nilai yang dikembalikan tersedia.

Marko Topolnik
sumber
21

Pertama-tama, sumber terbaik untuk memahami IMO ini adalah ceramah "Menyelam Mendalam ke Coroutines" oleh Roman Elizarov.

Coroutine atau fungsi ditangguhkan?

Memanggil menangguhkan ing fungsi menangguhkan s coroutine, yang berarti benang saat dapat mulai menjalankan coroutine lain. Jadi, coroutine dikatakan ditangguhkan daripada fungsinya.

Faktanya, situs panggilan untuk fungsi penangguhan disebut "titik penangguhan" karena alasan ini.

Coroutine mana yang ditangguhkan?

Mari kita lihat kode Anda dan uraikan apa yang terjadi:

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

Bagian luar asyncmemulai coroutine. Saat dipanggil computation(), bagian dalam asyncmemulai coroutine kedua. Kemudian, panggilan untuk await()menunda pelaksanaan luar async coroutine, sampai pelaksanaan dalam async coroutine 's berakhir.

Anda bahkan dapat melihatnya dengan satu utas: utas akan mengeksekusi bagian luar asyncawal, kemudian memanggil computation()dan mencapai bagian dalam async. Pada titik ini, badan asinkron bagian dalam dilewati, dan utas terus mengeksekusi bagian luar asynchingga mencapai await(). await()adalah "titik suspensi", karena awaitmerupakan fungsi penangguhan. Ini berarti bahwa coroutine luar ditangguhkan, dan dengan demikian thread mulai mengeksekusi yang dalam. Setelah selesai, ia kembali untuk mengeksekusi ujung luar async.

Apakah suspend berarti bahwa sementara coroutine asinkron luar sedang menunggu (menunggu) hingga coroutine komputasi dalam selesai, itu (coroutine asinkron luar) menganggur (oleh karena itu nama menangguhkan) dan mengembalikan utas ke kumpulan utas, dan ketika coroutine komputasi anak selesai , itu (coroutine asinkron luar) bangun, mengambil utas lain dari kolam dan melanjutkan?

Ya, tepatnya.

Cara ini sebenarnya dicapai adalah dengan mengubah setiap fungsi penangguhan menjadi mesin keadaan, di mana setiap "keadaan" sesuai dengan titik suspensi di dalam fungsi penangguhan ini. Di bawah kap, fungsi tersebut dapat dipanggil beberapa kali, dengan informasi tentang titik penangguhan mana yang harus mulai dijalankan (Anda harus benar-benar menonton video yang saya tautkan untuk info lebih lanjut tentang itu).

Joffrey
sumber
3
Jawaban yang bagus, saya merindukan penjelasan yang sangat mendasar tentang coroutine.
bernardo.g
Mengapa itu tidak diterapkan dalam bahasa lain? Atau apakah saya melewatkan sesuatu? Saya memikirkan solusi itu begitu lama, senang Kotlin memilikinya, tetapi tidak yakin mengapa TS atau Rust memiliki sesuatu seperti itu
PEZO
Coroutine sumur @PEZO sudah ada sejak lama. Kotlin tidak menemukannya, tetapi sintaks dan pustaka membuatnya bersinar. Go memiliki goroutine, JavaScript dan TypeScript memiliki janji. Satu-satunya perbedaan adalah detail sintaks untuk menggunakannya. Saya merasa cukup menjengkelkan / mengganggu asyncfungsi JS yang ditandai dengan cara ini, namun tetap mengembalikan Promise.
Joffrey
Maaf, komentar saya kurang jelas. Saya mengacu pada kata kunci suspend. Ini tidak sama dengan async.
PEZO
Terima kasih telah menunjuk ke video Roman. Emas murni.
Denounce'IN
8

Saya telah menemukan bahwa cara terbaik untuk memahaminya suspendadalah dengan membuat analogi antara thiskata kunci dan coroutineContextproperti.

Fungsi Kotlin dapat dideklarasikan sebagai lokal atau global. Fungsi lokal secara ajaib memiliki akses ke thiskata kunci sementara global tidak.

Fungsi Kotlin dapat dideklarasikan sebagai suspendatau memblokir. suspendfungsi secara ajaib memiliki akses ke coroutineContextproperti sementara fungsi pemblokiran tidak.

Masalahnya adalah: coroutineContextproperti dideklarasikan seperti properti "normal" di Kotlin stdlib tetapi deklarasi ini hanyalah sebuah rintisan untuk keperluan dokumentasi / navigasi. Bahkan coroutineContextyang builtin properti intrinsik yang berarti di bawah tenda compiler sihir menyadari properti ini seperti itu menyadari kata kunci bahasa.

Apa yang thisdilakukan kata kunci untuk fungsi lokal adalah apa yang coroutineContextdilakukan properti untuk suspendfungsi: ia memberikan akses ke konteks eksekusi saat ini.

Jadi, Anda perlu suspendmendapatkan akses ke coroutineContextproperti - contoh konteks coroutine yang saat ini dijalankan

Dmitry Kolesnikovich
sumber
5

Saya ingin memberi Anda contoh sederhana tentang konsep kelanjutan. Inilah yang dilakukan fungsi penangguhan, dapat membekukan / menangguhkan dan kemudian melanjutkan / melanjutkan. Berhenti memikirkan coroutine dalam kaitannya dengan utas dan Semaphore. Anggap saja dalam istilah kelanjutan dan bahkan kait panggilan balik.

Agar jelas, coroutine dapat dijeda dengan menggunakan suatu suspendfungsi. mari selidiki ini:

Di android kita bisa melakukan ini sebagai contoh:

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

Kode di atas mencetak yang berikut ini:

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

bayangkan itu bekerja seperti ini:

masukkan deskripsi gambar di sini

Jadi fungsi yang saat ini Anda luncurkan tidak berhenti, hanya coroutine yang akan ditangguhkan sementara itu berlanjut. Utas tidak dihentikan sementara dengan menjalankan fungsi penangguhan.

Saya pikir situs ini dapat membantu Anda menyelesaikan masalah dan merupakan referensi saya.

Mari lakukan sesuatu yang keren dan bekukan fungsi penangguhan kita di tengah iterasi. Kami akan melanjutkannya nantionResume

Simpan variabel yang dipanggil continuationdan kita akan memuatnya dengan objek lanjutan coroutines untuk kita:

var continuation: CancellableContinuation<String>? = null

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

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

Sekarang, mari kembali ke fungsi yang ditangguhkan dan membuatnya berhenti di tengah iterasi:

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

Kemudian di tempat lain seperti di onResume (misalnya):

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

Dan loop akan terus berlanjut. Cukup rapi untuk mengetahui bahwa kita dapat membekukan fungsi penangguhan kapan saja dan melanjutkannya setelah beberapa waktu berlalu. Anda juga dapat melihat saluran

j2emanue
sumber
4

Karena banyak jawaban bagus sudah ada, saya ingin memposting contoh yang lebih sederhana untuk orang lain.

runBlocking kasus penggunaan:

  • myMethod () adalah suspendfungsi
  • runBlocking { }memulai Coroutine dengan cara memblokir. Ini mirip dengan cara kami memblokir utas normal dengan Threadkelas dan memberi tahu utas yang diblokir setelah kejadian tertentu.
  • runBlocking { }tidak memblokir arus mengeksekusi benang, sampai coroutine (tubuh antara {}) akan selesai

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

Output ini:

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

kasus penggunaan peluncuran :

  • launch { } memulai coroutine secara bersamaan.
  • Ini berarti bahwa saat kita menentukan peluncuran, coroutine memulai eksekusi pada workerthread.
  • The workerbenang dan benang luar (dari mana kita disebut launch { }) keduanya berjalan bersamaan. Secara internal, JVM dapat melakukan Preemptive Threading
  • Saat kami membutuhkan banyak tugas untuk dijalankan secara paralel, kami dapat menggunakan ini. Ada scopesyang menentukan masa pakai coroutine. Jika kita tentukan GlobalScope, coroutine akan bekerja sampai masa pakai aplikasi berakhir.

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

Keluaran ini:

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 dan kasus penggunaan menunggu :

  • Ketika kita memiliki banyak tugas untuk dilakukan dan mereka bergantung pada penyelesaian orang lain, asyncdan awaitakan membantu.
  • Misalnya, pada kode di bawah ini, ada 2fungsi suspend myMethod () dan myMethod2 (). myMethod2()harus dieksekusi hanya setelah penyelesaian penuh myMethod() ATAU myMethod2() tergantung pada hasil myMethod(), kita dapat menggunakan asyncdanawait
  • asyncmemulai coroutine secara paralel mirip dengan launch. Namun, ini memberikan cara untuk menunggu satu coroutine sebelum memulai coroutine lain secara paralel.
  • Begitulah await(). asyncmengembalikan sebuah instance dari Deffered<T>. Takan menjadi Unitdefault. Ketika kita perlu menunggu untuk setiap async's selesai, kita perlu panggilan .await()pada Deffered<T>contoh yang async. Seperti pada contoh di bawah ini, kami memanggil innerAsync.await()yang menyiratkan bahwa eksekusi akan ditangguhkan sampai innerAsyncselesai. Kita dapat mengamati hal yang sama pada keluarannya. Yang innerAsyncselesai pertama, yang panggilan myMethod(). Dan kemudian async innerAsync2dimulai berikutnya , yang memanggilmyMethod2()

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

Output ini:

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
Kushal
sumber