ฟังก์ชัน Suspend หมายถึงอะไรใน Kotlin Coroutine


119

ฉันกำลังอ่าน Kotlin Coroutine และรู้ว่ามันขึ้นอยู่กับsuspendฟังก์ชัน แต่suspendหมายความว่าอย่างไร?

โครูทีนหรือฟังก์ชันถูกระงับ?

จากhttps://kotlinlang.org/docs/reference/coroutines.html

โดยทั่วไปโครูทีนคือการคำนวณที่สามารถระงับได้โดยไม่ต้องบล็อกเธรด

ฉันได้ยินคนพูดว่า "ระงับฟังก์ชัน" บ่อยๆ แต่ฉันคิดว่ามันเป็นโครูทีนที่ถูกระงับเพราะรอให้ฟังก์ชันเสร็จสิ้น? "ระงับ" มักจะหมายถึง "หยุดการทำงาน" ในกรณีนี้โครูทีนไม่ได้ใช้งาน

🤔เราควรบอกว่าโครูทีนถูกระงับหรือไม่?

โครูทีนใดถูกระงับ

จากhttps://kotlinlang.org/docs/reference/coroutines.html

หากต้องการดำเนินการเปรียบเทียบต่อไป await () อาจเป็นฟังก์ชันระงับ (ดังนั้นจึงสามารถเรียกได้จากภายในบล็อก async {}) ที่ระงับโครูทีนจนกว่าการคำนวณบางส่วนจะเสร็จสิ้นและส่งคืนผลลัพธ์:

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

🤔มีข้อความว่า "ระงับโครูทีนจนกว่าการคำนวณบางอย่างจะเสร็จสิ้น" แต่โครูทีนก็เหมือนด้ายที่มีน้ำหนักเบา ดังนั้นหากโครูทีนถูกระงับการคำนวณจะทำได้อย่างไร?

เราเห็นawaitว่าถูกเรียกcomputationดังนั้นมันอาจจะเป็นasyncผลตอบแทนDeferredซึ่งหมายความว่ามันสามารถเริ่มต้นโครูทีนอื่นได้

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

🤔คำพูดบอกว่าระงับโครูทีน หมายถึงโครูsuspendทีนชั้นนอกasyncหรือโครูsuspendทีนชั้นในcomputation?

ไม่suspendหมายถึงว่าในขณะที่ด้านนอกasynccoroutine กำลังรอ ( await) สำหรับภายในcomputationcoroutine จนจบมัน (นอกasynccoroutine) Idles (เพราะฉะนั้นชื่อระงับ) และผลตอบแทนด้ายสระว่ายน้ำด้ายและเมื่อเด็กcomputationเสร็จสิ้น coroutine มัน (นอกasynccoroutine ) ตื่นขึ้นมาใช้ด้ายอีกอันจากสระว่ายน้ำแล้วไปต่อ?

เหตุผลที่ฉันพูดถึงเธรดนี้เป็นเพราะhttps://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html

เธรดจะถูกส่งกลับไปยังพูลในขณะที่โครูทีนกำลังรอและเมื่อการรอเสร็จสิ้นโครูทีนจะดำเนินการต่อบนเธรดที่ว่างในพูล

คำตอบ:


116

ฟังก์ชันการระงับเป็นศูนย์กลางของทุกอย่าง ฟังก์ชันการระงับเป็นเพียงฟังก์ชันที่สามารถหยุดชั่วคราวและดำเนินการต่อได้ในภายหลัง พวกเขาสามารถดำเนินการทำงานที่ยาวนานและรอให้เสร็จสิ้นโดยไม่ปิดกั้น

ไวยากรณ์ของฟังก์ชันระงับจะคล้ายกับฟังก์ชันปกติยกเว้นการเพิ่มsuspendคีย์เวิร์ด สามารถใช้พารามิเตอร์และมีประเภทผลตอบแทน อย่างไรก็ตามฟังก์ชันการระงับสามารถเรียกใช้โดยฟังก์ชันการระงับอื่นหรือภายในโครูทีนเท่านั้น

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

Continuation<T>ภายใต้ประทุนระงับฟังก์ชั่นจะถูกแปลงโดยรวบรวมฟังก์ชั่นอื่นโดยไม่คำหลักระงับการที่จะใช้เวลาพารามิเตอร์นอกเหนือจากประเภท ตัวอย่างเช่นฟังก์ชันข้างต้นจะถูกแปลงโดยคอมไพเลอร์เป็น:

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

Continuation<T> เป็นอินเทอร์เฟซที่มีฟังก์ชันสองฟังก์ชันที่ถูกเรียกให้กลับมาทำงานโครูทีนโดยมีค่าส่งคืนหรือมีข้อยกเว้นหากเกิดข้อผิดพลาดขณะที่ฟังก์ชันถูกระงับ

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

5
เผยอาถรรพ์อีก! เยี่ยมมาก!
WindRider

17
ฉันสงสัยว่าฟังก์ชันนี้หยุดชั่วคราวได้อย่างไร พวกเขามักจะบอกว่าsuspend funสามารถหยุดชั่วคราวได้ แต่อย่างไรกันแน่?
WindRider

2
@WindRider หมายความว่าเธรดปัจจุบันเริ่มเรียกใช้โครูทีนอื่น ๆ และจะกลับมาที่เธรดนี้ในภายหลัง
Joffrey

2
ฉันได้ค้นพบกลไก "ลึกลับ" แล้ว สามารถเปิดเผยได้อย่างง่ายดายด้วยความช่วยเหลือของ Tools> Kotlin> Bytecode> Decompile btn มันแสดงให้เห็นว่า "จุดระงับ" ถูกนำไปใช้อย่างไร - ผ่านการดำเนินการต่อและอื่น ๆ ทุกคนสามารถมองหาตัวคุณเอง
WindRider

4
@buzaa นี่คือคำพูดจากปี 2017 โดย Roman Elizarov ที่อธิบายในระดับ bytecode
Marko Topolnik

31

เพื่อให้เข้าใจว่าการระงับโครูทีนหมายถึงอะไรฉันขอแนะนำให้คุณอ่านรหัสนี้:

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

ผู้Unconfinedมอบหมายงานโครูทีนช่วยขจัดความมหัศจรรย์ของการส่งโครูทีนและช่วยให้เราสามารถโฟกัสโดยตรงไปที่โครูทีนที่เปลือยเปล่า

โค้ดภายในlaunchบล็อกจะเริ่มทำงานทันทีบนเธรดปัจจุบันโดยเป็นส่วนหนึ่งของการlaunchโทร สิ่งที่เกิดขึ้นมีดังนี้:

  1. ประเมิน val a = a()
  2. โซ่นี้ถึงb()suspendCoroutine
  3. ฟังก์ชันb()เรียกใช้บล็อกที่ส่งผ่านไปsuspendCoroutineแล้วส่งกลับCOROUTINE_SUSPENDEDค่าพิเศษ ค่านี้ไม่สามารถสังเกตได้ผ่านโมเดลการเขียนโปรแกรม Kotlin แต่นั่นคือสิ่งที่วิธีการคอมไพล์ของ Java ทำ
  4. ฟังก์ชั่นa()เมื่อเห็นค่าที่ส่งคืนนี้ตัวมันเองก็ส่งคืนค่านี้เช่นกัน
  5. launchบล็อกไม่เหมือนกันและการควบคุมในขณะนี้กลับไปที่เส้นหลังที่launchภาวนา:10.downTo(0)...

โปรดทราบว่า ณ จุดนี้คุณมีผลเหมือนกับว่าโค้ดภายในlaunchบล็อกและfun mainโค้ดของคุณกำลังทำงานพร้อมกัน มันเกิดขึ้นเพียงว่าทั้งหมดนี้เกิดขึ้นในเธรดเนทีฟเพียงชุดเดียวดังนั้นการlaunchบล็อกจึงถูก "ระงับ"

ตอนนี้ภายในforEachรหัสการวนซ้ำโปรแกรมจะอ่านสิ่งcontinuationที่b()ฟังก์ชันเขียนและresumesมีค่า10เป็น resume()จะดำเนินการในลักษณะดังกล่าวว่าจะเป็นถ้าsuspendCoroutineโทรกลับมาพร้อมกับค่าที่คุณผ่าน. b()ดังนั้นคุณก็พบว่าตัวเองอยู่ตรงกลางของการดำเนินการ ค่าที่คุณส่งผ่านไปยังresume()ได้รับมอบหมายให้และตรวจสอบกับi 0หากยังไม่ได้ศูนย์while (true)ห่วงไปในภายในb()อีกครั้งถึงsuspendCoroutineจุดที่คุณโทรส่งกลับและตอนนี้คุณผ่านไปอีกหนึ่งขั้นตอนในการวนลูปresume() forEach()สิ่งนี้จะดำเนินต่อไปจนกว่าคุณจะดำเนินการต่อ0จากนั้นprintlnคำสั่งจะทำงานและโปรแกรมจะเสร็จสมบูรณ์

การวิเคราะห์ข้างต้นควรให้สัญชาตญาณที่สำคัญแก่คุณว่า "การระงับโครูทีน" หมายถึงการคืนการควบคุมกลับไปที่การlaunchเรียกใช้ด้านในสุด(หรือโดยทั่วไปแล้วตัวสร้างโครูทีน ) ถ้า coroutine ระงับอีกครั้งหลังจากที่กลับมาทำงานที่สิ้นสุดการโทรและการควบคุมกลับไปยังผู้โทรของresume()resume()

การมีผู้มอบหมายงานโครูทีนทำให้เหตุผลนี้มีความชัดเจนน้อยลงเนื่องจากส่วนใหญ่ส่งรหัสของคุณไปยังชุดข้อความอื่นทันที ในกรณีนี้เรื่องราวข้างต้นเกิดขึ้นในเธรดอื่นและผู้มอบหมายงาน coroutine ยังจัดการcontinuationวัตถุเพื่อให้สามารถดำเนินการต่อได้เมื่อมีค่าส่งคืน


23

ก่อนอื่นแหล่งที่ดีที่สุดในการทำความเข้าใจ IMO นี้คือการพูดคุยเรื่อง"Deep Dive into Coroutines"โดย Roman Elizarov

โครูทีนหรือฟังก์ชันถูกระงับ?

โทรระงับไอเอ็นจีฟังก์ชั่นระงับs coroutine ที่มีความหมายเธรดปัจจุบันสามารถเริ่มต้นการดำเนินการ coroutine อื่น ดังนั้นcoroutineกล่าวจะถูกระงับมากกว่าฟังก์ชั่น

ในความเป็นจริงไซต์การโทรของฟังก์ชันการระงับเรียกว่า "จุดระงับ" ด้วยเหตุนี้

โครูทีนใดถูกระงับ

มาดูโค้ดของคุณและแจกแจงสิ่งที่เกิดขึ้น:

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

ด้านนอกasyncเริ่มโครูทีน เมื่อโทรออกcomputation()ด้านในจะasyncเริ่มโครูทีนที่สอง จากนั้นการเรียกร้องให้await()ระงับการทำงานของโครูทีนด้านนอก asyncจนกว่าการดำเนินการโครูทีนด้านใน asyncจะสิ้นสุดลง

คุณยังสามารถดูได้ว่ามีหัวข้อเดียว: ด้ายจะดำเนินการด้านนอกasyncของจุดเริ่มต้นแล้วโทรและการเข้าถึงด้านในcomputation() asyncณ จุดนี้ร่างกายของ async ภายในคือข้ามและด้ายยังคงดำเนินการด้านนอกจนกว่าจะถึง async เป็น "จุดระงับ" เนื่องจากเป็นฟังก์ชันระงับ ซึ่งหมายความว่าโครูทีนด้านนอกถูกระงับดังนั้นเธรดจึงเริ่มทำงานด้านใน เมื่อเสร็จก็กลับมาดำเนินการในตอนท้ายของภายนอกawait()await()awaitasync

การหยุดชั่วคราวหมายความว่าในขณะที่โครูทีน async ด้านนอกกำลังรอ (รอ) เพื่อให้โครูทีนการคำนวณด้านในเสร็จสิ้นมัน (โครูทีน async ด้านนอก) ไม่ทำงาน (ดังนั้นการระงับชื่อ) และส่งคืนเธรดไปยังเธรดพูลและเมื่อโครูทีนการคำนวณลูกเสร็จสิ้น มัน (โครูทีน async ด้านนอก) ตื่นขึ้นใช้ด้ายอื่นจากสระว่ายน้ำและดำเนินการต่อ?

ใช่แน่นอน

วิธีนี้สามารถทำได้โดยการเปลี่ยนฟังก์ชันการระงับทั้งหมดให้เป็นเครื่องสเตตโดยที่ "สถานะ" แต่ละอันจะตรงกับจุดกันกระเทือนภายในฟังก์ชันการระงับนี้ ภายใต้ประทุนสามารถเรียกใช้ฟังก์ชันนี้ได้หลายครั้งพร้อมข้อมูลเกี่ยวกับจุดระงับที่ควรเริ่มดำเนินการ (คุณควรดูวิดีโอที่ฉันลิงก์เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับสิ่งนั้น)


4
คำตอบที่ดีฉันคิดถึงคำอธิบายพื้นฐานแบบนั้นจริงๆเมื่อพูดถึงโครูทีน
bernardo.g

เหตุใดจึงไม่มีการใช้งานในภาษาอื่น หรือฉันขาดอะไรไป? ฉันคิดเกี่ยวกับวิธีแก้ปัญหานั้นมานานแล้วดีใจที่ Kotlin มีอยู่ แต่ไม่แน่ใจว่าทำไม TS หรือ Rust ถึงมีอะไรแบบนั้น
PEZO

@PEZO ดีโครูทีนมีมานานแล้ว Kotlin ไม่ได้คิดค้นสิ่งเหล่านี้ แต่ไวยากรณ์และไลบรารีทำให้พวกเขาเปล่งประกาย Go มี goroutines, JavaScript และ TypeScript มีสัญญา ข้อแตกต่างเพียงอย่างเดียวคือในรายละเอียดของไวยากรณ์ที่จะใช้ ฉันคิดว่ามันค่อนข้างน่ารำคาญ / รบกวนสำหรับasyncฟังก์ชั่นของ JS ที่ถูกทำเครื่องหมายด้วยวิธีนี้ แต่ก็ยังคืนสัญญา
Joffrey

ขออภัยความคิดเห็นของฉันไม่ชัดเจน ฉันหมายถึงคำหลักระงับ มันไม่เหมือนกับ async
PEZO

ขอบคุณที่ชี้ไปที่วิดีโอของ Roman ทองคำบริสุทธิ์.
Denounce'IN

8

ฉันพบว่าวิธีที่ดีที่สุดในการทำความเข้าใจsuspendคือการเปรียบเทียบระหว่างthisคีย์เวิร์ดและcoroutineContextคุณสมบัติ

ฟังก์ชั่น Kotlin สามารถประกาศเป็นแบบโลคัลหรือโกลบอล ฟังก์ชั่นท้องถิ่นสามารถเข้าถึงthisคำสำคัญได้อย่างน่าอัศจรรย์ในขณะที่ทั่วโลกไม่สามารถเข้าถึงได้

ฟังก์ชั่น Kotlin สามารถประกาศเป็นsuspendหรือบล็อกได้ suspendฟังก์ชั่นเข้าถึงcoroutineContextคุณสมบัติได้อย่างน่าอัศจรรย์ในขณะที่ฟังก์ชั่นการบล็อกไม่ทำ

สิ่งนี้คือ: coroutineContextคุณสมบัติ ถูกประกาศเหมือนคุณสมบัติ "ปกติ"ใน Kotlin stdlib แต่การประกาศนี้เป็นเพียงจุดเริ่มต้นสำหรับวัตถุประสงค์ด้านเอกสาร / การนำทาง ในความcoroutineContextเป็นจริงเป็นคุณสมบัติที่แท้จริงในตัวซึ่งหมายความว่าภายใต้เวทย์มนตร์คอมไพเลอร์ฝากระโปรงตระหนักถึงคุณสมบัตินี้เช่นรู้คำหลักภาษา

สิ่งที่thisคำหลักไม่สำหรับฟังก์ชั่นในท้องถิ่นคือสิ่งที่coroutineContextคุณสมบัติไม่สำหรับsuspendฟังก์ชั่น: มันช่วยให้การเข้าถึงบริบทปัจจุบันของการดำเนินการ

ดังนั้นคุณต้องsuspendได้รับการเข้าถึงcoroutineContextคุณสมบัติ - ตัวอย่างของบริบท Coroutine ที่ดำเนินการในปัจจุบัน


6

ฉันอยากจะยกตัวอย่างง่ายๆของแนวคิดเรื่องความต่อเนื่อง นี่คือสิ่งที่ฟังก์ชั่น Suspend ทำมันสามารถหยุด / หยุดชั่วคราวจากนั้นจะดำเนินการต่อ / ดำเนินการต่อ หยุดคิดถึงโครูทีนในแง่ของเธรดและเซมาฟอร์ คิดในแง่ของความต่อเนื่องและแม้แต่การเรียกกลับ

เพื่อความชัดเจนโครูทีนสามารถหยุดชั่วคราวได้โดยใช้suspendฟังก์ชัน ให้ตรวจสอบสิ่งนี้:

ใน Android เราสามารถทำได้เช่น:

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

รหัสด้านบนพิมพ์ข้อมูลต่อไปนี้:

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

ลองนึกดูว่ามันทำงานได้เช่นนี้:

ป้อนคำอธิบายภาพที่นี่

ดังนั้นฟังก์ชั่นปัจจุบันที่คุณเปิดใช้งานไม่ได้หยุดเพียงแค่โครูทีนจะหยุดทำงานในขณะที่ดำเนินการต่อ เธรดไม่หยุดชั่วคราวโดยการรันฟังก์ชัน Suspend

ฉันคิดว่าไซต์นี้สามารถช่วยคุณได้ตรงประเด็นและเป็นข้อมูลอ้างอิงของฉัน

มาทำสิ่งที่เจ๋ง ๆ และหยุดฟังก์ชัน Suspend ไว้ระหว่างการทำซ้ำ เราจะดำเนินการต่อในภายหลังonResume

จัดเก็บตัวแปรที่เรียกcontinuationและเราจะโหลดด้วยวัตถุความต่อเนื่องของโครูทีนสำหรับเรา:

var continuation: CancellableContinuation<String>? = null

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

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

ตอนนี้กลับไปที่ฟังก์ชันที่ถูกระงับของเราและทำให้มันค้างระหว่างการทำซ้ำ:

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

จากนั้นที่อื่นเช่นใน onResume (ตัวอย่าง):

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

และลูปจะดำเนินต่อไป มันค่อนข้างเรียบร้อยที่จะรู้ว่าเราสามารถหยุดฟังก์ชั่น Suspend ได้ทุกเมื่อและกลับมาทำงานต่อหลังจากผ่านไปสักระยะ คุณยังสามารถดูช่อง


4

เนื่องจากมีคำตอบที่ดีอยู่แล้วฉันจึงอยากจะโพสต์ตัวอย่างที่ง่ายกว่านี้สำหรับคนอื่น ๆ

runBlockingกรณีการใช้งาน:

  • myMethod () คือsuspendฟังก์ชัน
  • runBlocking { }เริ่ม Coroutine ด้วยวิธีการปิดกั้น คล้ายกับการที่เราบล็อกเธรดปกติด้วยThreadคลาสและการแจ้งเตือนเธรดที่ถูกบล็อกหลังจากเหตุการณ์บางอย่าง
  • runBlocking { }ไม่บล็อกเธรดการดำเนินการปัจจุบันจนกว่าโครูทีน (ตัวระหว่าง{}) จะเสร็จสมบูรณ์

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

ผลลัพธ์นี้:

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

เปิดใช้กรณีการใช้งาน:

  • launch { } เริ่มโครูทีนพร้อมกัน
  • ซึ่งหมายความว่าเมื่อเราระบุการเรียกใช้โครูทีนจะเริ่มดำเนินการกับworkerเธรด
  • workerด้ายและด้ายนอก (จากที่เราเรียกว่าlaunch { }) ทั้งสองจะทำงานควบคู่กันไป ภายใน JVM อาจดำเนินการPreemptive Threading
  • เมื่อเราต้องการให้ทำงานหลายอย่างพร้อมกันเราสามารถใช้สิ่งนี้ได้ มีscopesระบุอายุการใช้งานโครูทีน หากเราระบุโคGlobalScopeรูทีนจะทำงานจนกว่าอายุการใช้งานจะสิ้นสุดลง

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

ผลลัพธ์นี้:

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และรอกรณีการใช้งาน:

  • เมื่อเรามีงานหลายอย่างที่ต้องทำและขึ้นอยู่กับความสำเร็จของผู้อื่นasyncและawaitจะช่วยได้
  • ตัวอย่างเช่นในโค้ดด้านล่างมี2ฟังก์ชัน suspend myMethod () และ myMethod2 () myMethod2()ควรได้รับการดำเนินการหลังจากเสร็จสมบูรณ์ของmyMethod() OR เท่านั้น myMethod2()ขึ้นอยู่กับผลลัพธ์ที่myMethod()เราสามารถใช้asyncและawait
  • asyncเริ่ม coroutine launchในที่คล้ายกันขนาน แต่มันเป็นวิธีการรอโครูทีนหนึ่งตัวก่อนที่จะเริ่มโครูทีนอื่นแบบคู่ขนาน
  • await()ลักษณะที่เป็น ผลตอบแทนที่ได้ตัวอย่างของasync จะเป็นค่าเริ่มต้น เมื่อเราต้องรอใด ๆ's เสร็จเราจำเป็นต้องโทรในตัวอย่างของการที่ เช่นเดียวกับในตัวอย่างด้านล่างเราเรียกซึ่งหมายความว่าการดำเนินการจะถูกระงับจนกว่าจะเสร็จสิ้น เราสามารถสังเกตสิ่งเดียวกันในเอาต์พุต รับแรกเสร็จซึ่งเรียก จากนั้นเริ่มต้นครั้งต่อไปซึ่งเรียกDeffered<T>TUnitasync.await()Deffered<T>asyncinnerAsync.await()innerAsyncinnerAsyncmyMethod()async innerAsync2myMethod2()

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

ผลลัพธ์นี้:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.