Kotlin Academy · บทเรียน

การเก็บกวาดด้วย finally และ NonCancellable

คืนทรัพยากรใน coroutine ที่ถูกยกเลิกด้วย finally และ withContext(NonCancellable)

บทเรียน 3 จาก 413 ขั้นตอน

การเก็บกวาดด้วย finally และ NonCancellable เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

การยกเลิกและ finally

เมื่อโคโรทีนถูกยกเลิก จุดระงับการทำงานจะโยน CancellationException โค้ดในบล็อก finally จะทำงานเสมอ แต่ตัว finally เองจะทำงานในบริบทโคโรทีนที่ถูกยกเลิกแล้ว

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            println("finally: cleaning up")
        }
    }
    delay(50)
    job.cancel()
    job.join()
}

ปัญหา: การระงับการทำงานใน finally

หากพยายามใช้ delay หรือทำการระงับการทำงานอื่นภายใน finally ของโคโรทีนที่ถูกยกเลิก ระบบจะโยนข้อยกเว้นทันที เพราะบริบทถูกยกเลิกไปแล้ว

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            // delay(100) // throws CancellationException here!
            println("Sync cleanup OK; async cleanup needs NonCancellable")
        }
    }
    delay(50); job.cancel(); job.join()
}

withContext(NonCancellable)

ครอบการเรียกที่มีการระงับการทำงานใน finally ด้วย withContext(NonCancellable) เพื่อให้การเรียกเหล่านั้นทำงานในบริบทที่ยกเลิกไม่ได้ แม้ขอบเขตแม่จะถูกยกเลิกแล้ว

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } finally {
            withContext(NonCancellable) {
                delay(100)  // OK: runs despite cancellation
                println("Async cleanup complete")
            }
        }
    }
    delay(50); job.cancel(); job.join()
}

ควรใช้ NonCancellable เมื่อใด

ใช้ NonCancellable สำหรับการปิดการเชื่อมต่อฐานข้อมูล การเขียนบันทึกให้เสร็จ การส่งเหตุการณ์การวิเคราะห์ หรือการล้างข้อมูลแบบอะซิงโครนัสใด ๆ ที่ต้องทำให้เสร็จไม่ว่าจะถูกยกเลิกหรือไม่

import kotlinx.coroutines.*
suspend fun closeConnection() {
    println("Closing DB connection...")
    delay(50) // simulate async close
    println("Connection closed")
}
fun main() = runBlocking {
    val job = launch {
        try { delay(1000) }
        finally { withContext(NonCancellable) { closeConnection() } }
    }
    delay(50); job.cancel(); job.join()
}

NonCancellable ไม่ใช่ขอบเขต

NonCancellable คือ Job ที่สามารถส่งให้ withContext ได้ อย่าใช้โดยตรงเป็นขอบเขตโคโรทีน แต่ให้ใช้เฉพาะในการล้างข้อมูลภายใน finally เท่านั้น

import kotlinx.coroutines.*
fun main() = runBlocking {
    // WRONG usage:
    // launch(NonCancellable) { ... } // launches a coroutine that cannot be cancelled
    // RIGHT usage:
    launch {
        try { delay(500) }
        finally {
            withContext(NonCancellable) {
                println("Safe cleanup")
            }
        }
    }.also { delay(50); it.cancel(); it.join() }
}

การล้างทรัพยากรหลายรายการ

เชื่อมการดำเนินการล้างข้อมูลหลายรายการไว้ภายในบล็อก withContext(NonCancellable) เดียว

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            delay(1000)
        } finally {
            withContext(NonCancellable) {
                delay(20); println("Step 1: flush cache")
                delay(20); println("Step 2: close socket")
                delay(20); println("Step 3: log audit")
            }
        }
    }.also { delay(50); it.cancel(); it.join() }
}

รูปแบบ try/catch/finally

ใช้ try-catch ร่วมกับข้อผิดพลาดทางธุรกิจ และใช้ finally สำหรับการล้างข้อมูล นี่คือรูปแบบมาตรฐานสำหรับการทำงานที่มีโครงสร้าง

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            delay(100)
            throw RuntimeException("business error")
        } catch (e: RuntimeException) {
            println("Handling: ${e.message}")
        } finally {
            withContext(NonCancellable) {
                println("Always cleanup")
            }
        }
    }.join()
}

ลำดับการล้างข้อมูล

บล็อก Finally จะทำงานในลำดับย้อนกลับจากการซ้อนกัน บล็อก try-finally ภายนอกจะทำงานหลังบล็อกภายใน เช่นเดียวกับโค้ด Java/คอตลินทั่วไป

import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        try {
            try {
                delay(1000)
            } finally { println("Inner cleanup") }
        } finally { println("Outer cleanup") }
    }.also { delay(50); it.cancel(); it.join() }
}

use() สำหรับทรัพยากร AutoCloseable

สำหรับทรัพยากร AutoCloseable ให้ใช้ส่วนขยาย use { } ของคอตลิน ซึ่งจะเรียก close() โดยอัตโนมัติ แม้เกิดข้อยกเว้นหรือการยกเลิก

import kotlinx.coroutines.*
class Connection : AutoCloseable {
    init { println("Opened") }
    override fun close() { println("Closed") }
    suspend fun query(): String { delay(50); return "result" }
}
fun main() = runBlocking {
    val result = Connection().use { it.query() }
    println(result)
}

การบันทึกเหตุผลของการยกเลิก

ดึงข้อความการยกเลิกจาก CancellationException ใน catch หรือ finally เพื่อบันทึกว่าเหตุใดโคโรทีนจึงหยุดทำงาน

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        try {
            delay(1000)
        } catch (e: CancellationException) {
            println("Cancelled: ${e.message}")
            throw e
        }
    }
    delay(50)
    job.cancel("user navigated away")
    job.join()
}

การล้าง CoroutineScope ด้วย invokeOnCompletion

Job.invokeOnCompletion จะลงทะเบียนฟังก์ชันเรียกกลับที่ทำงานเมื่องานเสร็จสิ้นไม่ว่าด้วยเหตุผลใด ซึ่งเป็นทางเลือกแทน finally สำหรับการล้างข้อมูลในระดับแม่

import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        delay(1000)
    }
    job.invokeOnCompletion { cause ->
        if (cause != null) println("Ended with: ${cause::class.simpleName}")
        else println("Completed normally")
    }
    delay(50)
    job.cancel()
    job.join()
}

ตรวจสอบอย่างรวดเร็ว

withContext(NonCancellable) ในบล็อก finally มีจุดประสงค์อะไร

ทบทวน

finally จะทำงานเสมอเมื่อมีการยกเลิก ให้ครอบการเรียกที่มีการระงับการทำงานภายใน finally ด้วย withContext(NonCancellable) เพื่อให้การเรียกเหล่านั้นเสร็จสิ้นแม้เกิดการยกเลิก ใช้ use { } กับทรัพยากร AutoCloseable

เริ่มต้นได้ฟรี

เรียนรู้ Kotlin ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
51
บทเรียน
203

คำถามที่พบบ่อย

บทเรียน “การเก็บกวาดด้วย finally และ NonCancellable” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเก็บกวาดด้วย finally และ NonCancellable” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเก็บกวาดด้วย finally และ NonCancellable”

คืนทรัพยากรใน coroutine ที่ถูกยกเลิกด้วย finally และ withContext(NonCancellable) คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การเก็บกวาดด้วย finally และ NonCancellable” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม

ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การยกเลิกร่วมมือกัน: isActive และ ensureActive
  2. withTimeout และ withTimeoutOrNull
  3. การเก็บกวาดด้วย finally และ NonCancellable
  4. การส่งต่อการยกเลิกในลำดับชั้น Coroutine
← กลับไปที่ Kotlin Academy