DispatchQueue เป็นคิวพื้นฐานของ Grand Central Dispatch (GCD) สำหรับจัดการงานแบบอะซิงโครนัสใน iOS และ macOS ตามเอกสาร Apple Developer Documentation, 2026 DispatchQueue ทำให้การจัดการเธรดเป็นนามธรรมจากนักพัฒนาผ่านคิวแบบ serial และ concurrent GCD จะกระจายงานไปยังพูลเธรดของระบบโดยอัตโนมัติ ขจัดความจำเป็นในการสร้างและทำลายเธรดด้วยตนเอง
ประเด็นสำคัญ
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 นำเธรดกลับมาใช้ใหม่จากพูล ลดค่าใช้จ่ายในการสร้างเธรด
Grand Central Dispatch ประกอบด้วยสามองค์ประกอบหลัก: คิว (DispatchQueue), กลุ่ม (DispatchGroup) และ เซมาฟอร์ (DispatchSemaphore) คิวเป็นองค์ประกอบหลักที่รับงานในรูปแบบของบล็อกโค้ด DispatchGroup ซิงโครไนซ์การดำเนินการของหลายงาน ในขณะที่ DispatchSemaphore จำกัดการเข้าถึงทรัพยากรที่ใช้ร่วมกันเป็นจำนวนเธรดที่เฉพาะเจาะจง
แต่ละคิว GCD เชื่อมโยงกับคลาส QoS (Quality of Service) ที่เฉพาะเจาะจง ซึ่งแจ้งให้ระบบทราบถึงความสำคัญของงาน ระบบใช้ QoS เพื่อกระจายเวลา CPU ระหว่างคิว โดยให้ลำดับความสำคัญกับงานที่สำคัญกว่า — เช่น การอัปเดต UI หรือการจัดการการสัมผัสของผู้ใช้
คิวแบบ serial ดำเนินงานตามลำดับอย่างเคร่งครัด ทีละงาน หากวางสามงานในคิวแบบ serial งานที่สองจะเริ่มต้นหลังจากงานแรกเสร็จสมบูรณ์เท่านั้น คิวแบบ serial ใช้สำหรับซิงโครไนซ์การเข้าถึงทรัพยากรที่ใช้ร่วมกัน — ตัวอย่างเช่น อาร์เรย์ที่ถูกแก้ไขจากหลายส่วนของโค้ด
คิวแบบ concurrent ทำงานหลายอย่างพร้อมกัน โดยกระจายไปยังเธรดที่มีอยู่ในพูลของระบบ งานในคิวแบบ concurrent เริ่มต้นในลำดับ FIFO แต่เสร็จสิ้นในลำดับที่สุ่มหากเวลาดำเนินการต่างกัน คิวแบบ concurrent ไม่รับประกันลำดับการเสร็จสิ้น — รับประกันเฉพาะลำดับการเริ่มต้นเท่านั้น
| พารามิเตอร์ | คิวแบบ Serial | คิวแบบ Concurrent |
|---|---|---|
| ลำดับการดำเนินการ | ตามลำดับอย่างเคร่งครัด | ขนาน |
| จำนวนเธรด | หนึ่ง | หลายจากพูล GCD |
| การใช้งาน | ปกป้องทรัพยากรที่ใช้ร่วมกัน | การคำนวณอิสระ |
| คิวหลัก | ใช่ (เธรดหลัก) | ไม่ |
| ความเสี่ยง deadlock | สูงเมื่อใช้ sync ในคิวเดียวกัน | ต่ำ |
คิวแบบ serial เหมาะสำหรับงานที่แก้ไข สถานะที่ใช้ร่วมกัน — การเขียนไฟล์ การอัปเดตโมเดลข้อมูล หรือการทำงานกับ Core Data การใช้คิวแบบ serial รับประกันว่าโค้ดสองส่วนจะไม่แก้ไขข้อมูลเดียวกันพร้อมกัน กำจัดสภาวะการแข่งขันโดยไม่ต้องใช้ล็อกเพิ่มเติม
คิวแบบ concurrent เหมาะสำหรับงานที่ไม่พึ่งพากัน: การโหลดหลายภาพ คำขอเครือข่ายแบบขนาน หรือการประมวลผลข้อมูลเป็นชุด GCD จะตัดสินใจโดยอัตโนมัติว่าจะทำงานกี่งานพร้อมกันตามจำนวนคอร์ CPU และโหลดของระบบปัจจุบัน
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 ต่ำที่สุดที่ยังให้เวลาดำเนินการที่ยอมรับได้
เมื่อโหลดภาพเพื่อแสดงทันที ให้ใช้ .userInitiated — ผู้ใช้คาดหวังผลลัพธ์ สำหรับการโหลดหน้าจอถัดไปล่วงหน้า .utility ก็เพียงพอ การซิงโครไนซ์พื้นหลังกับเซิร์ฟเวอร์ดำเนินการด้วย .background ลดผลกระทบต่องานที่กำลังทำงาน
DispatchGroup ช่วยให้ติดตามความสมบูรณ์ของกลุ่มงานได้ เมื่องานทั้งหมดในกลุ่มเสร็จสมบูรณ์ GCD จะเรียกใช้ตัวจัดการ notify ในคิวที่ระบุ ซึ่งมีประโยชน์โดยเฉพาะอย่างยิ่งเมื่อโหลดทรัพยากรอิสระหลายรายการ — ข้อมูลโปรไฟล์ รายชื่อเพื่อน และการตั้งค่า — ซึ่งอินเทอร์เฟซควรอัปเดตหลังจากได้รับข้อมูลทั้งหมดเท่านั้น
DispatchGroup รองรับการเรียกแบบซิงโครนัส wait() ซึ่งบล็อกเธรดปัจจุบันจนกว่างานทั้งหมดจะเสร็จสมบูรณ์ สะดวกเมื่อโค้ดไม่สามารถดำเนินการต่อได้หากไม่มีผลลัพธ์ของกลุ่ม รูปแบบอะซิงโครนัส notify() จะเรียก closure ในคิวที่ระบุหลังจากงานทั้งหมดเสร็จสมบูรณ์ โดยไม่บล็อกเธรดที่เรียก
DispatchSemaphore ควบคุมการเข้าถึงทรัพยากรโดยจำกัดจำนวนการเข้าถึงพร้อมกัน เซมาฟอร์ที่มีค่าเริ่มต้นเป็น 3 อนุญาตให้ทำงานแบบขนานได้สูงสุดสามงาน การเรียก wait() จะลดค่าตัวนับ signal() จะเพิ่มค่า หากตัวนับเป็นศูนย์ เธรดจะถูกบล็อกจนกว่าทรัพยากรจะพร้อมใช้งาน
มาดูตัวอย่างเชิงปฏิบัติสามตัวอย่างของการใช้ DispatchQueue ใน Swift ตัวอย่างแรกสาธิตการเรียก async พื้นฐานพร้อมกลับไปยังเธรดหลัก ตัวอย่างที่สองแสดงการซิงโครไนซ์ผ่านคิวแบบ serial และตัวอย่างที่สามใช้ DispatchGroup สำหรับคำขอแบบขนาน
DispatchQueue.main เป็นคิวแบบ serial ของเธรดหลัก ซึ่งมีไว้สำหรับการดำเนินการ UI เท่านั้น ใช้เสมอเพื่ออัปเดตอินเทอร์เฟซหลังจากทำงานพื้นหลังเสร็จสิ้น
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
let data = self.fetchData()
DispatchQueue.main.async {
self.updateUI(with: data)
}
}
การสร้างคิวแบบ serial ที่กำหนดเองด้วยตัวระบุเฉพาะ ซิงโครไนซ์ การเข้าถึงอาร์เรย์ที่เปลี่ยนแปลงได้ การดำเนินการอ่านและเขียนทั้งหมดผ่านคิวเดียว กำจัดสภาวะการแข่งขัน
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 ช่วยให้เริ่มงานหลายงานในคิวแบบ concurrent และรับการแจ้งเตือนเมื่อทั้งหมดเสร็จสมบูรณ์ มีประโยชน์เมื่อโหลดข้อมูลสำหรับหน้าจอโปรไฟล์
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()
}
Deadlock เมื่อเรียก sync ในคิวแบบ serial เป็นข้อผิดพลาดที่พบบ่อยที่สุด หากงานในคิวแบบ serial เรียก queue.sync ในคิวเดียวกัน เธรดจะถูกบล็อกตลอดไป คิวรอให้งานปัจจุบันเสร็จสิ้น และงานรอให้การเรียก sync เสร็จสิ้น — การบล็อกซึ่งกันและกันแบบคลาสสิก
การดำเนินการทั้งหมดกับ UIKit ต้องดำเนินการบนเธรดหลัก Xcode ตรวจจับข้อผิดพลาดเหล่านี้ในโหมด Debug ผ่าน Main Thread Checker ในบิวด์ Release จะนำไปสู่พฤติกรรมที่คาดเดาไม่ได้: แอนิเมชันไม่เริ่มทำงาน UI ไม่อัปเดต และอาจเกิดการครา
การสร้างคิวแบบกำหนดเองหลายร้อยคิวแทนการใช้คิวส่วนกลางเป็น แอนติแพทเทิร์น แต่ละคิวใช้ทรัพยากรระบบ สำหรับงานส่วนใหญ่ คิวแบบ concurrent ส่วนกลางที่มีระดับ QoS ต่างกันและคิวแบบ serial หนึ่งหรือสองคิวสำหรับซิงโครไนซ์ข้อมูลที่ใช้ร่วมกันก็เพียงพอ
เมื่อดำเนินการงานลูปที่ใช้ทรัพยากรมากในคิวพื้นหลังโดยไม่มี autoreleasepool หน่วยความจำจะเพิ่มขึ้นจนกว่าลูปทั้งหมดจะสิ้นสุด ARC จะปล่อยวัตถุเมื่อออกจาก autorelease pool เท่านั้น ห่อการวนซ้ำลูปใน autoreleasepool { } เพื่อคืนหน่วยความจำอย่างทันท่วงที
คำถามที่พบบ่อย
OperationQueue สร้างขึ้นบน GCD แต่ให้ API ระดับสูงกว่าพร้อมการพึ่งพาการดำเนินการ KVO และการรองรับการยกเลิก DispatchQueue เป็นคิวระดับต่ำสำหรับงาน async ง่าย ๆ โดยไม่มีการจัดการการพึ่งพา
GCD ไม่รองรับการหยุดงานที่กำลังทำงานอยู่ เมธอด suspend() จะหยุดเฉพาะงานใหม่เท่านั้น งานปัจจุบันทำงานจนจบ การยกเลิกต้องมีการตรวจสอบแฟล็กด้วยตนเองภายในโค้ดของงาน
สำหรับคำขอหลักที่ต้องแสดงผลทันที — .userInitiated สำหรับการโหลดข้อมูลล่วงหน้า — .utility สำหรับการซิงโครไนซ์พื้นหลัง — .background
GCD ไม่ได้กำหนดจำนวนเธรด พูลเธรด ปรับขนาดแบบไดนามิกภายใต้โหลด โดยพิจารณาจากคอร์ CPU โหลดปัจจุบัน และ QoS ของแต่ละงาน จำนวนสูงสุดถูกจำกัดโดยระบบ
UIKit ไม่ปลอดภัยต่อเธรด — คลาสทั้งหมดต้องถูกเรียกจากเธรดหลักเท่านั้น การละเมิดทำให้เกิดพฤติกรรมที่คาดเดาไม่ได้ การอัปเดตตกหล่น และการคราใน Production
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม