DispatchQueue: คืออะไร คิว GCD และพื้นฐานการทำงานแบบหลายเธรด

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

DispatchQueue เป็นคิวพื้นฐานของ Grand Central Dispatch (GCD) สำหรับจัดการงานแบบอะซิงโครนัสใน iOS และ macOS ตามเอกสาร Apple Developer Documentation, 2026 DispatchQueue ทำให้การจัดการเธรดเป็นนามธรรมจากนักพัฒนาผ่านคิวแบบ serial และ concurrent GCD จะกระจายงานไปยังพูลเธรดของระบบโดยอัตโนมัติ ขจัดความจำเป็นในการสร้างและทำลายเธรดด้วยตนเอง

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

  • DispatchQueue เป็นนามธรรมหลักของ GCD สำหรับการดำเนินการโค้ดแบบอะซิงโครนัสใน iOS
  • คิวแบบ serial ดำเนินงานตามลำดับอย่างเคร่งครัด กำจัดสภาวะการแข่งขัน
  • คิวแบบ concurrent ทำงานหลายอย่างพร้อมกันผ่านพูลเธรดของระบบ
  • QoS กำหนดลำดับความสำคัญของงาน — ตั้งแต่ userInteractive ถึง background
  • DispatchQueue.main เป็นคิวเดียวสำหรับอัปเดต UIKit บนเธรดหลัก

DispatchQueue และ Grand Central Dispatch คืออะไร

DispatchQueue เป็นวัตถุของเฟรมเวิร์ก Grand Central Dispatch (GCD) ที่จัดการการดำเนินงานในคิวเธรดของระบบหรือแบบกำหนดเอง Grand Central Dispatch เป็นไลบรารีระดับต่ำของ Apple ที่พร้อมใช้งานตั้งแต่ iOS 4 และ macOS 10.6 ซึ่งทำให้การจัดการเธรดเป็นนามธรรมจากนักพัฒนาอย่างสมบูรณ์ GCD ใช้พูลเธรดของระบบปฏิบัติการและปรับขนาดจำนวนเธรดโดยอัตโนมัติตามโหลดของอุปกรณ์

นักพัฒนาไม่จำเป็นต้องสร้างและทำลายเธรดด้วยตนเอง — GCD จัดการงานนี้ โดยให้ API ที่เรียบง่ายผ่าน DispatchQueue งานในรูปแบบของ closure จะถูกส่งไปยังคิวผ่านเมธอด sync หรือ async ในกรณีแรก เธรดที่เรียกจะถูกบล็อกจนกว่างานจะเสร็จสมบูรณ์ ในกรณีที่สอง การดำเนินการจะดำเนินต่อไปทันที

ตามข้อมูลของ Apple (2026) GCD ใช้พูลเธรดของระบบที่ปรับตามจำนวนคอร์และโหลด CPU ปัจจุบัน คิวแบบ concurrent ไม่ได้สร้างเธรดใหม่สำหรับแต่ละงาน — GCD นำเธรดกลับมาใช้ใหม่จากพูล ลดค่าใช้จ่ายในการสร้างเธรด

สถาปัตยกรรม GCD

Grand Central Dispatch ประกอบด้วยสามองค์ประกอบหลัก: คิว (DispatchQueue), กลุ่ม (DispatchGroup) และ เซมาฟอร์ (DispatchSemaphore) คิวเป็นองค์ประกอบหลักที่รับงานในรูปแบบของบล็อกโค้ด DispatchGroup ซิงโครไนซ์การดำเนินการของหลายงาน ในขณะที่ DispatchSemaphore จำกัดการเข้าถึงทรัพยากรที่ใช้ร่วมกันเป็นจำนวนเธรดที่เฉพาะเจาะจง

แต่ละคิว GCD เชื่อมโยงกับคลาส QoS (Quality of Service) ที่เฉพาะเจาะจง ซึ่งแจ้งให้ระบบทราบถึงความสำคัญของงาน ระบบใช้ QoS เพื่อกระจายเวลา CPU ระหว่างคิว โดยให้ลำดับความสำคัญกับงานที่สำคัญกว่า — เช่น การอัปเดต UI หรือการจัดการการสัมผัสของผู้ใช้

คิวแบบ Serial และ Concurrent: การเปรียบเทียบ

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

คิวแบบ concurrent ทำงานหลายอย่างพร้อมกัน โดยกระจายไปยังเธรดที่มีอยู่ในพูลของระบบ งานในคิวแบบ concurrent เริ่มต้นในลำดับ FIFO แต่เสร็จสิ้นในลำดับที่สุ่มหากเวลาดำเนินการต่างกัน คิวแบบ concurrent ไม่รับประกันลำดับการเสร็จสิ้น — รับประกันเฉพาะลำดับการเริ่มต้นเท่านั้น

พารามิเตอร์คิวแบบ Serialคิวแบบ Concurrent
ลำดับการดำเนินการตามลำดับอย่างเคร่งครัดขนาน
จำนวนเธรดหนึ่งหลายจากพูล GCD
การใช้งานปกป้องทรัพยากรที่ใช้ร่วมกันการคำนวณอิสระ
คิวหลักใช่ (เธรดหลัก)ไม่
ความเสี่ยง deadlockสูงเมื่อใช้ sync ในคิวเดียวกันต่ำ

เมื่อใดควรเลือกคิวแบบ serial

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

เมื่อใดควรเลือกคิวแบบ concurrent

คิวแบบ concurrent เหมาะสำหรับงานที่ไม่พึ่งพากัน: การโหลดหลายภาพ คำขอเครือข่ายแบบขนาน หรือการประมวลผลข้อมูลเป็นชุด GCD จะตัดสินใจโดยอัตโนมัติว่าจะทำงานกี่งานพร้อมกันตามจำนวนคอร์ CPU และโหลดของระบบปัจจุบัน

Quality of Service: ลำดับความสำคัญของการดำเนินงาน

QoS (Quality of Service) เป็นกลไกของ GCD ที่แจ้งให้ระบบปฏิบัติการทราบถึงความสำคัญและความเร่งด่วนของงาน ระบบใช้ QoS สำหรับการจัดตารางเวลาเธรด: งานที่มี QoS สูงกว่าได้รับเวลา CPU มากกว่าและเริ่มต้นเร็วกว่า ค่า QoS จะถูกส่งเมื่อสร้างคิวหรือส่งงานเฉพาะ

GCD มีคลาส QoS ห้าคลาส .userInteractive — ลำดับความสำคัญสูงสุดสำหรับงานที่เกี่ยวข้องกับ UI .userInitiated — สำหรับงานที่เริ่มต้นโดยผู้ใช้ .utility — สำหรับงานพื้นหลังที่แสดงความคืบหน้า .background — สำหรับงานที่ผู้ใช้มองไม่เห็น .default — ระดับกลางระหว่าง userInitiated และ utility ใช้เป็นค่าเริ่มต้น

ตามข้อมูลของ Apple (2026) การเลือก QoS ที่ไม่ถูกต้องเป็นหนึ่งในสาเหตุทั่วไปของปัญหา ประสิทธิภาพ การเรียกใช้การดาวน์โหลดพื้นหลังด้วย QoS .userInteractive จะใช้ทรัพยากรของ UI ทำให้เกิดการกระตุกขนาดเล็กในแอนิเมชัน ขอแนะนำให้เลือก QoS ต่ำที่สุดที่ยังให้เวลาดำเนินการที่ยอมรับได้

ตัวอย่างการใช้ QoS

เมื่อโหลดภาพเพื่อแสดงทันที ให้ใช้ .userInitiated — ผู้ใช้คาดหวังผลลัพธ์ สำหรับการโหลดหน้าจอถัดไปล่วงหน้า .utility ก็เพียงพอ การซิงโครไนซ์พื้นหลังกับเซิร์ฟเวอร์ดำเนินการด้วย .background ลดผลกระทบต่องานที่กำลังทำงาน

DispatchGroup และเซมาฟอร์: การซิงโครไนซ์งาน

DispatchGroup ช่วยให้ติดตามความสมบูรณ์ของกลุ่มงานได้ เมื่องานทั้งหมดในกลุ่มเสร็จสมบูรณ์ GCD จะเรียกใช้ตัวจัดการ notify ในคิวที่ระบุ ซึ่งมีประโยชน์โดยเฉพาะอย่างยิ่งเมื่อโหลดทรัพยากรอิสระหลายรายการ — ข้อมูลโปรไฟล์ รายชื่อเพื่อน และการตั้งค่า — ซึ่งอินเทอร์เฟซควรอัปเดตหลังจากได้รับข้อมูลทั้งหมดเท่านั้น

DispatchGroup รองรับการเรียกแบบซิงโครนัส wait() ซึ่งบล็อกเธรดปัจจุบันจนกว่างานทั้งหมดจะเสร็จสมบูรณ์ สะดวกเมื่อโค้ดไม่สามารถดำเนินการต่อได้หากไม่มีผลลัพธ์ของกลุ่ม รูปแบบอะซิงโครนัส notify() จะเรียก closure ในคิวที่ระบุหลังจากงานทั้งหมดเสร็จสมบูรณ์ โดยไม่บล็อกเธรดที่เรียก

DispatchSemaphore สำหรับจำกัดการทำงานพร้อมกัน

DispatchSemaphore ควบคุมการเข้าถึงทรัพยากรโดยจำกัดจำนวนการเข้าถึงพร้อมกัน เซมาฟอร์ที่มีค่าเริ่มต้นเป็น 3 อนุญาตให้ทำงานแบบขนานได้สูงสุดสามงาน การเรียก wait() จะลดค่าตัวนับ signal() จะเพิ่มค่า หากตัวนับเป็นศูนย์ เธรดจะถูกบล็อกจนกว่าทรัพยากรจะพร้อมใช้งาน

ตัวอย่างโค้ดกับ DispatchQueue ใน Swift

มาดูตัวอย่างเชิงปฏิบัติสามตัวอย่างของการใช้ DispatchQueue ใน Swift ตัวอย่างแรกสาธิตการเรียก async พื้นฐานพร้อมกลับไปยังเธรดหลัก ตัวอย่างที่สองแสดงการซิงโครไนซ์ผ่านคิวแบบ serial และตัวอย่างที่สามใช้ DispatchGroup สำหรับคำขอแบบขนาน

การเรียก async พื้นฐานพร้อมกลับไปยัง main

DispatchQueue.main เป็นคิวแบบ serial ของเธรดหลัก ซึ่งมีไว้สำหรับการดำเนินการ UI เท่านั้น ใช้เสมอเพื่ออัปเดตอินเทอร์เฟซหลังจากทำงานพื้นหลังเสร็จสิ้น

swift
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
    let data = self.fetchData()
    DispatchQueue.main.async {
        self.updateUI(with: data)
    }
}

คิวแบบ serial สำหรับปกป้องทรัพยากรที่ใช้ร่วมกัน

การสร้างคิวแบบ serial ที่กำหนดเองด้วยตัวระบุเฉพาะ ซิงโครไนซ์ การเข้าถึงอาร์เรย์ที่เปลี่ยนแปลงได้ การดำเนินการอ่านและเขียนทั้งหมดผ่านคิวเดียว กำจัดสภาวะการแข่งขัน

swift
let serialQueue = DispatchQueue(label: "com.app.items")
var items: [Int] = []

serialQueue.async {
    items.append(1)
}
serialQueue.async {
    let last = items.last
    DispatchQueue.main.async {
        print("Last item: \(last)")
    }
}

DispatchGroup สำหรับคำขอแบบขนาน

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

swift
let group = DispatchGroup()
let worker = DispatchQueue.global()

worker.async(group: group) { self.loadProfile() }
worker.async(group: group) { self.loadFriends() }
worker.async(group: group) { self.loadSettings() }

group.notify(queue: DispatchQueue.main) {
    self.showCompleteUI()
}

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

Deadlock เมื่อเรียก sync ในคิวแบบ serial เป็นข้อผิดพลาดที่พบบ่อยที่สุด หากงานในคิวแบบ serial เรียก queue.sync ในคิวเดียวกัน เธรดจะถูกบล็อกตลอดไป คิวรอให้งานปัจจุบันเสร็จสิ้น และงานรอให้การเรียก sync เสร็จสิ้น — การบล็อกซึ่งกันและกันแบบคลาสสิก

การอัปเดต UI จากเธรดพื้นหลัง

การดำเนินการทั้งหมดกับ UIKit ต้องดำเนินการบนเธรดหลัก Xcode ตรวจจับข้อผิดพลาดเหล่านี้ในโหมด Debug ผ่าน Main Thread Checker ในบิวด์ Release จะนำไปสู่พฤติกรรมที่คาดเดาไม่ได้: แอนิเมชันไม่เริ่มทำงาน UI ไม่อัปเดต และอาจเกิดการครา

การสร้างคิวแบบกำหนดเองมากเกินไป

การสร้างคิวแบบกำหนดเองหลายร้อยคิวแทนการใช้คิวส่วนกลางเป็น แอนติแพทเทิร์น แต่ละคิวใช้ทรัพยากรระบบ สำหรับงานส่วนใหญ่ คิวแบบ concurrent ส่วนกลางที่มีระดับ QoS ต่างกันและคิวแบบ serial หนึ่งหรือสองคิวสำหรับซิงโครไนซ์ข้อมูลที่ใช้ร่วมกันก็เพียงพอ

การละเลย autoreleasepool ในลูป

เมื่อดำเนินการงานลูปที่ใช้ทรัพยากรมากในคิวพื้นหลังโดยไม่มี autoreleasepool หน่วยความจำจะเพิ่มขึ้นจนกว่าลูปทั้งหมดจะสิ้นสุด ARC จะปล่อยวัตถุเมื่อออกจาก autorelease pool เท่านั้น ห่อการวนซ้ำลูปใน autoreleasepool { } เพื่อคืนหน่วยความจำอย่างทันท่วงที

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

ความแตกต่างระหว่าง DispatchQueue และ OperationQueue คืออะไร?

OperationQueue สร้างขึ้นบน GCD แต่ให้ API ระดับสูงกว่าพร้อมการพึ่งพาการดำเนินการ KVO และการรองรับการยกเลิก DispatchQueue เป็นคิวระดับต่ำสำหรับงาน async ง่าย ๆ โดยไม่มีการจัดการการพึ่งพา

สามารถบังคับหยุดงานใน DispatchQueue ได้หรือไม่?

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

ควรเลือก QoS ใดสำหรับคำขอเครือข่าย?

สำหรับคำขอหลักที่ต้องแสดงผลทันที — .userInitiated สำหรับการโหลดข้อมูลล่วงหน้า — .utility สำหรับการซิงโครไนซ์พื้นหลัง — .background

คิวแบบ concurrent ใช้กี่เธรด?

GCD ไม่ได้กำหนดจำนวนเธรด พูลเธรด ปรับขนาดแบบไดนามิกภายใต้โหลด โดยพิจารณาจากคอร์ CPU โหลดปัจจุบัน และ QoS ของแต่ละงาน จำนวนสูงสุดถูกจำกัดโดยระบบ

ทำไม DispatchQueue.main ถึงจำเป็นสำหรับ UIKit?

UIKit ไม่ปลอดภัยต่อเธรด — คลาสทั้งหมดต้องถูกเรียกจากเธรดหลักเท่านั้น การละเมิดทำให้เกิดพฤติกรรมที่คาดเดาไม่ได้ การอัปเดตตกหล่น และการคราใน Production

สรุป

  • DispatchQueue เป็นเครื่องมือหลักของ Grand Central Dispatch สำหรับงานอะซิงโครนัสใน iOS และ macOS
  • คิวแบบ serial ดำเนินงานตามลำดับ กำจัดสภาวะการแข่งขันโดยไม่ต้องล็อก
  • คิวแบบ concurrent ดำเนินงานแบบขนานผ่านพูลเธรดของระบบ
  • QoS กำหนดลำดับความสำคัญของงาน — ตั้งแต่ userInteractive ถึง background
  • DispatchGroup ซิงโครไนซ์งานขนานหลายงานด้วย notify บนเธรดหลัก
  • Deadlock เมื่อ sync ในคิวแบบ serial ที่ไม่ว่างเป็นข้อผิดพลาดร้ายแรงที่ต้องให้ความสนใจ
  • เธรดหลัก จำเป็นสำหรับ UIKit — อัปเดตอินเทอร์เฟซผ่าน DispatchQueue.main เท่านั้น

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

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

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

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