GCD 基本概念-Queue、QoS、WorkItem

Zhi-Hong Lin
8 min readNov 15, 2023

--

GCD 是一個管理隊列(queue)和工作項目(block)的系統,它將工作分發給系統中的執行緒(thread)以實現並行處理。以下會初步介紹使用 GCD 的基本概念

  • DispatchQueue
  • DispatchQoS
  • DeadLock
  • DispatchWorkItem

DispatchQueue

用於管理工作項目在 App 的主執行緒或後台執行緒以串行或並行方式執行,DispatchQueue 有三種形式,遵循(FIFO)先進先出。

建立 Custom Queue 時的 label 命名,官方建議使用反轉的 Domain name,例如:com.25sprout.custom

隊列有兩種類型

serialconcurrent 是用來描述隊列(queue)特性。

提交工作項目有兩種方式

syncasync是兩種主要方法,用於將工作項目(block)提交到隊列(queue)中。

總結:

DispatchQoS(quality of service)

用於指定調度的優先級。它允許你告訴系統關於任務的相對重要性,以便系統可以更好地管理資源分配和調度,以下說明由優先度高到低。

指定 Qos 有以下兩種方式

  1. 建立一個指定 Qos 的 Custom Queue,或者使用 global queue
let queue = DispatchQueue(label: "background", qos: .background)

DispatchQueue.global(qos: .background)

2. 在提交 block 的時候指定 Qos

queue.async(qos: .background) {

}

DeadLock

發生在兩個或多個執行緒互相等待對方釋放所持有的資源,從而陷入一種永久的等待狀態。

以下是使用 Serial Queue 為例,有兩種嵌套方式會造成 DeadLock ; 而若是 Concurrent Queue 則都不會造成DeadLock

let queue = DispatchQueue(label: "serial")
// 會造成 dead lock
queue.sync {
print("sync execution, thread: \\(Thread.current)")
queue.sync {
print("sync execution, thread: \\(Thread.current)")
}
}
// 會造成 dead lock
queue.async {
print("async execution, thread: \\(Thread.current)")
queue.sync {
print("sync execution, thread: \\(Thread.current)")
}
}
// 不會造成 dead lock
queue.sync {
print("sync execution, thread: \\(Thread.current)")
queue.async {
print("async execution, thread: \\(Thread.current)")
}
}
// 不會造成 dead lock
queue.async {
print("async execution, thread: \\(Thread.current)")
queue.async {
print("async execution, thread: \\(Thread.current)")
}
}

DispatchWorkItem

代表一個可以在隊列中執行的工作項目(block)。DispatchWorkItem 允許你將工作安排到隊列中,同時提供更多的控制選項,例如取消工作項目或等待工作項目的完成。

let queue = DispatchQueue(label: "com.example.myqueue")

// 創建一個 DispatchWorkItem
let workItem = DispatchWorkItem {
for i in 1...5 {
print("Task \\(i)")
sleep(1) // 模擬一些工作
}
}
// 將工作項目提交到隊列中
queue.async(execute: workItem)
// 你可以在稍後取消工作項目(如果需要的話)
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
if !workItem.isCancelled {
workItem.cancel()
print("Task cancelled")
}
}

Custom Queue 支持 DispatchWorkItem 設置 flags 為 .barrier,可以支持 barrier 之前的任務全部執行完畢後,再執行 .barrier 任務,最後再執行 .barrier 之後的任務,這樣處理可以保證執行緒安全。

(注意:Global Queue,flags 設置 .barrier 無效)

let item1 = DispatchWorkItem {
for i in 0...1{
print("item1 -> \\(i) thread: \\(Thread.current)")
}
}

let item2 = DispatchWorkItem {
for i in 0...1{
print("item2 -> \\(i) thread: \\(Thread.current)")
}
}
//給 item3 任務加 barrier
let item3 = DispatchWorkItem(flags: .barrier) {
for i in 0...1{
print("item3 barrier -> \\(i) thread: \\(Thread.current)")
}
}
let item4 = DispatchWorkItem {
for i in 0...1{
print("item4 -> \\(i) thread: \\(Thread.current)")
}
}
let item5 = DispatchWorkItem {
for i in 0...1{
print("item5 -> \\(i) thread: \\(Thread.current)")
}
}
let queue = DispatchQueue(label: "test", attributes: .concurrent)
queue.async(execute: item1)
queue.async(execute: item2)
queue.async(execute: item3)
queue.async(execute: item4)
queue.async(execute: item5)

以下是輸出結果:可以看到 item1 和 item2 在 item3 之前執行,item4 和 item5 在 item3 之後執行

item1 -> 0  thread: <NSThread: 0x600001710b40>{number = 4, name = (null)}
item2 -> 0 thread: <NSThread: 0x600001704a40>{number = 6, name = (null)}
item1 -> 1 thread: <NSThread: 0x600001710b40>{number = 4, name = (null)}
item2 -> 1 thread: <NSThread: 0x600001704a40>{number = 6, name = (null)}
item3 barrier -> 0 thread: <NSThread: 0x600001704a40>{number = 6, name = (null)}
item3 barrier -> 1 thread: <NSThread: 0x600001704a40>{number = 6, name = (null)}
item4 -> 0 thread: <NSThread: 0x600001704a40>{number = 6, name = (null)}
item5 -> 0 thread: <NSThread: 0x600001710b40>{number = 4, name = (null)}
item4 -> 1 thread: <NSThread: 0x600001704a40>{number = 6, name = (null)}
item5 -> 1 thread: <NSThread: 0x600001710b40>{number = 4, name = (null)}

--

--

No responses yet