CoroutineScope — คืออะไร วงจรชีวิต และการทำงานในคอรูน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-22 เวลาอ่าน: 10 นาที

CoroutineScope เป็นอินเทอร์เฟส Kotlin ที่กำหนดวงจรชีวิตของคอรูนและให้บริบทสำหรับเริ่มต้นคอรูนใหม่ ตาม เอกสารของ Kotlin, 2025 แต่ละอินสแตนซ์ของ CoroutineScope มี CoroutineContext และจัดการคอรูนทั้งหมดที่เริ่มต้นภายใน เมื่อ scope ถูกยกเลิก (cancel) คอรูนลูกทั้งหมดจะถูกยกเลิกโดยอัตโนมัติ ป้องกันหน่วยความจำรั่ว

ประเด็นสำคัญ

  • CoroutineScope — อินเทอร์เฟสที่มีฟิลด์ CoroutineContext เดียว กำหนดวงจรชีวิตของคอรูน
  • Job — องค์ประกอบบริบทที่รับผิดชอบการยกเลิก: การยกเลิก scope จะยกเลิกคอรูนลูกทั้งหมด
  • การทำงานพร้อมกันแบบมีโครงสร้าง — หลักการที่คอรูนลูกผูกพันกับ scope แม่
  • GlobalScope — scope ทั่วทั้งแอปพลิเคชันที่ไม่แนะนำเนื่องจากความเสี่ยงหน่วยความจำรั่ว
  • supervisorScope — scope พิเศษที่การยกเลิกคอรูนลูกหนึ่งไม่ยกเลิกตัวอื่น

CoroutineScope ใน Kotlin คืออะไร?

CoroutineScope เป็นอินเทอร์เฟสพื้นฐานจากไลบรารี kotlinx.coroutines ที่ทำหน้าที่เป็นคอนเทนเนอร์สำหรับคอรูน กำหนดขอบเขตของวงจรชีวิตคอรูน: เมื่อ scope เสร็จสมบูรณ์ คอรูนทั้งหมดภายในจะถูกยกเลิกโดยอัตโนมัติ

kotlin
public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

อินเทอร์เฟสมีเพียงฟิลด์เดียว — coroutineContext ผ่านฟิลด์นี้ scope จะจัดหาตัวจัดสรร (Dispatcher) งาน (Job) ตัวจัดการข้อยกเว้น และองค์ประกอบบริบทอื่น ๆ ให้กับคอรูนทั้งหมดที่เริ่มต้นภายใน

บทบาทในไลบรารี kotlinx.coroutines

ฟังก์ชันเริ่มต้นคอรูนทั้งหมด — launch, async, runBlocking — เป็นฟังก์ชันส่วนขยายบน CoroutineScope ซึ่งหมายความว่าสามารถเรียกได้เมื่อมีวัตถุ scope เท่านั้น การออกแบบนี้รับประกันว่าคอรูนแต่ละตัวมีแม่และวงจรชีวิตที่กำหนดไว้อย่างชัดเจน

CoroutineScope ใช้ที่ไหน

ใน Android แต่ละคอมโพเนนต์เชิงสถาปัตยกรรมมี scope ของตัวเอง: viewModelScope สำหรับ ViewModel, lifecycleScope สำหรับ Activity/Fragment ในแอปพลิเคชันเซิร์ฟเวอร์ scope สามารถผูกกับคำขอ HTTP หรือพูลการเชื่อมต่อฐานข้อมูล

CoroutineScope ทำงานอย่างไร: Job และการทำงานพร้อมกันแบบมีโครงสร้าง

การทำความเข้าใจการทำงานภายในของ CoroutineScope ต้องคุ้นเคยกับแนวคิดของ Job และหลักการของ การทำงานพร้อมกันแบบมีโครงสร้าง

Job — งานของคอรูน

คอรูนแต่ละตัวเมื่อเริ่มต้นจะส่งคืนวัตถุ Job (หรือ Deferred สำหรับ async) Job แทนงานที่มีวงจรชีวิตจำกัด: New, Active, Completing, Completed, Cancelling, Cancelled วัตถุ Job สร้างโครงสร้างแบบต้นไม้:

  • Job แม่ — scope ที่เริ่มต้นคอรูน
  • Job ลูก — คอรูนแต่ละตัวที่เริ่มต้นผ่าน launch/async
  • การยกเลิกแม่ → การยกเลิกลูกทั้งหมด
  • ข้อยกเว้นในลูก → การยกเลิกแม่ (ยกเว้นใน supervisorScope)

หลักการทำงานพร้อมกันแบบมีโครงสร้าง

การทำงานพร้อมกันแบบมีโครงสร้าง เป็นหลักการเชิงสถาปัตยกรรมหลักของ Kotlin Coroutines ที่วงจรชีวิตของคอรูนผูกกับวงจรชีวิตของ scope ซึ่งตรงกันข้ามกับโมเดล “ยิงแล้วลืม” ที่คอรูนยังคงมีชีวิตอยู่หลังจาก scope เสร็จสมบูรณ์ ข้อดีของการทำงานพร้อมกันแบบมีโครงสร้าง:

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

วงจรชีวิตของ CoroutineScope

เมื่อเรียก scope.cancel() Job ของ scope จะเปลี่ยนเป็นสถานะ Cancelled ซึ่งจะยกเลิก Job ลูกทั้งหมดแบบเรียกซ้ำ หลังจากการยกเลิก scope สามารถนำกลับมาใช้ใหม่ได้ก็ต่อเมื่อสร้างอินสแตนซ์ CoroutineScope ใหม่

การสร้างและกำหนดค่า CoroutineScope

คุณสามารถสร้าง CoroutineScope ผ่านฟังก์ชันโรงงานหรือโดยการใช้อินเทอร์เฟสในคลาสของคุณ มาดูทั้งสองวิธี

ฟังก์ชันโรงงาน CoroutineScope()

kotlin
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())

scope.launch {
    println("Running on ${Thread.currentThread().name}")
}

ฟังก์ชันโรงงานรับ CoroutineContext และสร้าง scope ด้วยบริบทที่ระบุ ตัวอย่างใช้ Dispatchers.Default สำหรับงานที่ใช้ CPU มาก และ SupervisorJob ซึ่งแยกข้อยกเว้นระหว่างคอรูนลูก

การใช้อินเทอร์เฟสผ่านคอมโพสิชัน

kotlin
class MyRepository {
    private val scope = CoroutineScope(Dispatchers.IO + Job())

    suspend fun fetchData(): Data = scope.async {
        api.getData()
    }.await()

    fun cleanup() {
        scope.cancel()
    }
}

เราเก็บ scope เป็นฟิลด์ของคลาสและเรียก cleanup ด้วยตนเองเพื่อยกเลิก วิธีนี้เหมาะสำหรับคอมโพเนนต์ที่มีวงจรชีวิตที่จัดการได้ — เช่น repository หรือ manager

การใช้งานผ่านการมอบหมาย

Kotlin อนุญาตให้มอบหมายการใช้งาน CoroutineScope ผ่านคีย์เวิร์ด by:

kotlin
class DataLoader : CoroutineScope by CoroutineScope(Dispatchers.IO) {
    fun load() {
        launch {
            // coroutine runs in DataLoader scope
        }
    }
}

วิธีนี้สะดวกเมื่อคลาสเองเป็น scope และต้องการให้เมธอดเริ่มต้นคอรูน อย่างไรก็ตาม ระวัง: คลาสสืบทอดเมธอดทั้งหมดของ CoroutineScope รวมถึง cancel ซึ่งอาจทำลายการห่อหุ้ม

GlobalScope กับ CoroutineScope ที่กำหนดเอง

GlobalScope เป็น CoroutineScope แบบซิงเกิลตันสำหรับทั้งแอปพลิเคชัน การใช้งานในโค้ดโปรดักชันไม่ได้รับการแนะนำอย่างเป็นทางการ

ปัญหาของ GlobalScope

  • ขาดการทำงานพร้อมกันแบบมีโครงสร้าง — คอรูนใน GlobalScope ไม่ผูกกับวงจรชีวิตของคอมโพเนนต์
  • หน่วยความจำรั่ว — คอรูนอาจทำงานต่อหลังจากปิด Activity/Fragment
  • การทดสอบยาก — GlobalScope ไม่สามารถแทนที่ในการทดสอบ
  • การใช้ทรัพยากรที่ไม่สามารถควบคุมได้ — คอรูนจำนวนมากอาจทำงานนานกว่าที่คาดไว้

เมื่อใดที่ GlobalScope เหมาะสม

JetBrains อนุญาต GlobalScope เฉพาะในสถานการณ์ที่หายาก: กระบวนการพื้นหลังระดับแอปพลิเคชัน ที่ควรมีชีวิตอยู่แม้หลังจากปิด Activity ทั้งหมดแล้ว (เช่น การซิงค์ข้อมูล การวิเคราะห์) แต่แม้ในกรณีเหล่านี้ ควรสร้าง scope ของคุณเองด้วย CoroutineScope(SupervisorJob())

คำแนะนำ

ใช้ CoroutineScope ที่กำหนดเอง เสมอพร้อมการจัดการวงจรชีวิตที่ชัดเจน ใน Android คือ viewModelScope และ lifecycleScope ในแอปพลิเคชันเซิร์ฟเวอร์ ให้สร้าง scope สำหรับแต่ละคำขอหรือพูลการเชื่อมต่อ

coroutineScope กับ supervisorScope: ความแตกต่าง

ทั้งสองฟังก์ชันเป็นฟังก์ชันระงับที่สร้าง scope ชั่วคราวสำหรับงานแบบขนาน แต่พฤติกรรมเมื่อมีข้อยกเว้นแตกต่างกันโดยพื้นฐาน

ลักษณะcoroutineScopesupervisorScope
พฤติกรรมเมื่อเกิดข้อผิดพลาดข้อยกเว้นในคอรูนลูกยกเลิกตัวอื่นทั้งหมดข้อยกเว้นในคอรูนลูกไม่ยกเลิกตัวอื่น
การแพร่กระจายข้อผิดพลาดใช่ ข้อยกเว้นแรกแพร่กระจายออกไปด้านนอกใช่ ข้อยกเว้นแรกแพร่กระจายออกไปด้านนอก
Job เริ่มต้นJob() — ลูกผูกกับแม่SupervisorJob() — ลูกไม่พึ่งพากัน
กรณีการใช้งานทั่วไปการดำเนินการอะตอมมิกหลายขั้นตอนงานขนานอิสระ (การโหลด UI)

เมื่อใดควรเลือก coroutineScope

ใช้ coroutineScope เมื่อการดำเนินการแบบขนานหลายรายการก่อให้เกิดการดำเนินการอะตอมมิกเดียว ตัวอย่างเช่น การโหลดข้อมูลจากสามเซิร์ฟเวอร์: หากคำขอหนึ่งล้มเหลว คำขอที่เหลือก็ไร้ความหมาย

kotlin
suspend fun loadProductPage(): ProductPage = coroutineScope {
    val product = async { api.getProduct() }
    val reviews = async { api.getReviews() }
    ProductPage(product.await(), reviews.await())
}

หาก getProduct หรือ getReviews โยนข้อยกเว้น — คอรูนทั้งสองจะถูกยกเลิก และข้อยกเว้นแพร่กระจายไปยังโค้ดที่เรียก

เมื่อใดควรเลือก supervisorScope

ใช้ supervisorScope เมื่อการดำเนินการแบบขนานไม่พึ่งพากัน ตัวอย่างเช่น การโหลดข้อมูลโปรไฟล์ในหลายส่วนอิสระ: หากส่วนคำแนะนำล้มเหลว ส่วนหัวโปรไฟล์และรายการเพื่อนควรแสดงผล

ข้อผิดพลาดทั่วไปเมื่อทำงานกับ CoroutineScope

มาดูข้อผิดพลาดที่พบบ่อยที่สุดของนักพัฒนาเมื่อใช้ CoroutineScope ใน Kotlin

ข้อผิดพลาด 1: ลืมยกเลิก scope

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

ข้อผิดพลาด 2: การใช้ GlobalScope ใน Activity หรือ Fragment

GlobalScope ไม่สนใจวงจรชีวิตของคอมโพเนนต์ Android คอรูนที่เริ่มต้นใน GlobalScope หลังจากปิด Activity จะยังคงทำงานและพยายามอัปเดต UI — ซึ่งจะทำให้เกิดการขัดข้อง ใช้ lifecycleScope สำหรับคอมโพเนนต์ UI เสมอ

ข้อผิดพลาด 3: การนำ scope ที่ถูกยกเลิกกลับมาใช้ใหม่

หลังจากเรียก cancel() แล้ว scope ไม่สามารถนำกลับมาใช้ใหม่ได้ — คอรูนทั้งหมดภายในเสร็จสมบูรณ์แล้ว สร้างอินสแตนซ์ CoroutineScope ใหม่ผ่านฟังก์ชันโรงงาน Job() ไม่สนับสนุนการเปิดใช้งานอีกครั้ง

ข้อผิดพลาด 4: การมอบหมายอินเทอร์เฟส CoroutineScope ไม่ถูกต้อง

เมื่อมอบหมายด้วย by คลาสจะได้รับเมธอด cancel() สาธารณะที่สามารถเรียกได้จากทุกที่ ทำลายการห่อหุ้ม เก็บ scope เป็นฟิลด์ส่วนตัวแทนที่จะมอบหมายอินเทอร์เฟส

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

CoroutineScope แตกต่างจาก CoroutineContext อย่างไร?

CoroutineScope เป็นอินเทอร์เฟสที่เป็นเจ้าของ CoroutineContext และรับผิดชอบวงจรชีวิตของคอรูน CoroutineContext คือชุดขององค์ประกอบ (ตัวจัดสรร job ตัวจัดการข้อผิดพลาด) ที่กำหนดว่าคอรูนทำงาน “อย่างไร” ความแตกต่างหนึ่ง: scope สร้างคอรูน ในขณะที่บริบทควบคุมพฤติกรรมของพวกมัน

สามารถสร้าง CoroutineScope ด้วย SupervisorJob ได้หรือไม่?

ได้ มันเป็นรูปแบบมาตรฐาน: CoroutineScope(Dispatchers.IO + SupervisorJob()) SupervisorJob ป้องกันการยกเลิกลูกโซ่ของคอรูนลูกเมื่อหนึ่งในนั้นโยนข้อยกเว้น มีประโยชน์สำหรับงานขนานอิสระที่ข้อผิดพลาดในหนึ่งไม่ควรหยุดตัวอื่น

CoroutineScope สามารถบรรจุคอรูนได้กี่ตัว?

ไม่มีข้อจำกัดเกี่ยวกับจำนวนคอรูนใน scope — จำกัดเฉพาะหน่วยความจำที่มีอยู่และการตั้งค่าตัวจัดสรรเท่านั้น ขีดจำกัดในทางปฏิบัติโดยทั่วไปคือ คอรูนที่ทำงานอยู่หลายพันตัวใน scope เดียว อย่างไรก็ตาม จำนวนคอรูนที่มากอาจบ่งบอกถึงปัญหาเชิงสถาปัตยกรรม

จะทดสอบโค้ดกับ CoroutineScope ได้อย่างไร?

วิธีที่ถูกต้องคือการส่ง scope ไปยังคลาสผ่านตัวสร้างหรือใช้ runBlockingTest / runTest จาก kotlinx-coroutines-test ในการทดสอบ คุณสามารถแทนที่ scope ด้วย TestCoroutineDispatcher และควบคุมการทำงานของคอรูนด้วยตนเอง

คอรูนสามารถมี scope ของตัวเองได้หรือไม่?

ไม่ scope เป็นคอนเทนเนอร์ภายนอกสำหรับคอรูน คอรูนเองไม่ใช่ scope อย่างไรก็ตาม ภายในคอรูนคุณสามารถสร้าง scope ใหม่ผ่าน coroutineScope หรือ supervisorScope เพื่อเริ่มต้นคอรูนลูกแบบขนาน

สรุป

  • CoroutineScope — อินเทอร์เฟสที่มีฟิลด์ coroutineContext กำหนดวงจรชีวิตของคอรูนที่เริ่มต้นภายใน
  • การทำงานพร้อมกันแบบมีโครงสร้าง — การยกเลิก scope จะยกเลิกคอรูนลูกทั้งหมดโดยอัตโนมัติ ป้องกันหน่วยความจำรั่ว
  • Job และ SupervisorJob — สองโหมดการจัดการข้อผิดพลาด: การยกเลิกลูกโซ่ (Job) และข้อผิดพลาดที่แยกกัน (SupervisorJob)
  • GlobalScope — ไม่แนะนำสำหรับโปรดักชันเนื่องจากขาดการผูกกับวงจรชีวิต
  • coroutineScope กับ supervisorScope — การดำเนินการขนานแบบอะตอมมิกกับงานขนานอิสระ
  • viewModelScope และ lifecycleScope — scope พร้อมใช้สำหรับ Android ยกเลิกอัตโนมัติเมื่อคอมโพเนนต์สิ้นสุด
  • ฟังก์ชันโรงงาน — วิธีที่ต้องการในการสร้าง scope ผ่าน CoroutineContext + การเรียก cancel อย่างชัดเจน

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม