信號量
定義
1、信號量是一種可用來控制訪問資源的數量的標識,設定了一個信號量,在線程訪問之前,加上信號量的處理,則可告知系統按照我們指定的信號量數量來執行多個線程。
2、信號量主要的 3 個函數:
// 創建信號量,參數:信號量的初值,如果小于 0 則會返回 NULL
dispatch_semaphore_create(信號量值)
// 等待降低信號量
dispatch_semaphore_wait(信號量,等待時間)
// 提高信號量
dispatch_semaphore_signal(信號量)
正常使用順序是先降低然后再提高,這兩個函數通常成對使用。
示例 1:
/// 信號量,semaphore.wait 會阻塞線程,所以最好放在 .async 中執行,否則會阻塞后續 UI 等任務的執行,造成卡頓。
func dispatchSignal() {
let semaphore = DispatchSemaphore(value: 1)
let queue = DispatchQueue.global(qos: .default)
queue.async {
print("--->wait 1")
_ = semaphore.wait(timeout: .distantFuture)
print("--->run task 1")
sleep(1)
print("--->complete task 1")
semaphore.signal()
print("--->wait 4")
_ = semaphore.wait(timeout: .distantFuture)
print("--->run task 4")
sleep(1)
print("--->complete task 4")
semaphore.signal()
}
queue.async {
print("--->wait 2")
_ = semaphore.wait(timeout: .distantFuture)
print("--->run task 2")
sleep(3)
print("--->complete task 2")
semaphore.signal()
}
queue.async {
print("--->wait 3")
_ = semaphore.wait(timeout: .distantFuture)
print("--->run task 3")
sleep(3)
print("--->complete task 3")
semaphore.signal()
}
print("===============")
}
// 輸出:
===============
--->wait 2
--->wait 1
--->wait 3
--->run task 2
--->complete task 2
--->run task 1
--->complete task 1
--->wait 4
--->run task 3
--->complete task 3
--->run task 4
--->complete task 4
// 注意:wait1、wait2、wait3 和 === 的輸出順序每次不是固定的。
示例 2:
override func viewDidLoad() {
super.viewDidLoad()
let semaphore = DispatchSemaphore(value: 0)
test1(semaphore: semaphore)
test2(semaphore: semaphore)
}
func test1(semaphore: DispatchSemaphore) {
let queue = DispatchQueue.global(qos: .default)
queue.async {
print("--->test1_1")
_ = semaphore.wait(timeout: .distantFuture)
print("--->test1_2")
semaphore.signal()
print("--->test1_3")
}
}
func test2(semaphore: DispatchSemaphore) {
print("--->test2_1")
// _ = semaphore.wait(timeout: .distantFuture)
print("--->test2")
}
輸出:
--->test2_1
--->test2
--->test1_1
// 注意:由于 semaphore 初始化時設置的值為 0,在 test1 方法中執行了 .wait 方法后,信號量值小于 0,造成該線程卡死,后續方法不再執行。
示例 3:
/// 使用信號量無序調用接口
func callApiSignal2() {
let semaphore = DispatchSemaphore(value: 0)
let queue = DispatchQueue.global()
queue.async {
print("--->callApiSignal2--run task 1")
sleep(3)
print("--->callApiSignal2--complete task 1")
semaphore.signal()
}
queue.async {
print("--->callApiSignal2--run task 2")
sleep(1)
print("--->callApiSignal2--complete task 2")
semaphore.signal()
}
queue.async {
print("--->callApiSignal2--run task 3")
sleep(4)
print("--->callApiSignal2--complete task 3")
semaphore.signal()
}
print("--->callApiSignal2--wait 01")
_ = semaphore.wait(timeout: .distantFuture)
print("--->callApiSignal2--wait 02")
_ = semaphore.wait(timeout: .distantFuture)
print("--->callApiSignal2--wait 03")
_ = semaphore.wait(timeout: .distantFuture)
print("--->callApiSignal2--refresh UI")
}
// 注意:由于 semaphore 信號量初始化時設定值為 0,當程序執行到主線程的第一個 .wait 方法時,主線程被阻塞了,該線程后續方法不再執行,但不影響上面幾個異步線程的執行。
當某個異步線程中模擬接口調用執行完畢,會執行 semaphore.signal(),從而將信號量值 +1,主線程中對應第一個 .wait 方法收到后線程阻塞被解除,從而會執行后續方法(打印wait 02)。
輸出:
--->callApiSignal2--wait 01
--->callApiSignal2--run task 1
--->callApiSignal2--run task 2
--->callApiSignal2--run task 3
--->callApiSignal2--complete task 2
--->callApiSignal2--wait 02
--->callApiSignal2--complete task 1
--->callApiSignal2--wait 03
--->callApiSignal2--complete task 3
--->callApiSignal2--refresh UI
示例 4:
/// 使用信號量有序調用接口
func callApiSignal1() {
let semaphore = DispatchSemaphore(value: 0)
let queue = DispatchQueue.global()
queue.async {
print("--->callApiSignal1--run task 1")
sleep(3)
print("--->callApiSignal1--complete task 1")
semaphore.signal()
}
print("--->callApiSignal1--wait 01")
_ = semaphore.wait(timeout: .distantFuture)
queue.async {
print("--->callApiSignal1--run task 2")
sleep(1)
print("--->callApiSignal1--complete task 2")
semaphore.signal()
}
print("--->callApiSignal1--wait 02")
_ = semaphore.wait(timeout: .distantFuture)
queue.async {
print("--->callApiSignal1--run task 3")
sleep(4)
print("--->callApiSignal1--complete task 3")
semaphore.signal()
}
print("--->callApiSignal1--wait 03")
_ = semaphore.wait(timeout: .distantFuture)
print("--->callApiSignal1--refresh UI")
}
輸出:
--->callApiSignal1--wait 01
--->callApiSignal1--run task 1
--->callApiSignal1--complete task 1
--->callApiSignal1--wait 02
--->callApiSignal1--run task 2
--->callApiSignal1--complete task 2
--->callApiSignal1--wait 03
--->callApiSignal1--run task 3
--->callApiSignal1--complete task 3
--->callApiSignal1--refresh UI
GCD Group 隊列組
示例 1:group 無序調用
/// 使用 group 無序調用,閉包里是同步方法,可以不使用enter、leave方法
func callApiGroup1() {
let group = DispatchGroup()
let queue = DispatchQueue.global()
queue.async(group: group, execute: {
print("--->callApiGroup1--run task1")
})
queue.async(group: group, execute: {
print("--->callApiGroup1--run task2")
})
queue.async(group: group, execute: {
print("--->callApiGroup1--run task3")
})
group.notify(queue: queue) {
print("--->callApiGroup1--notify--run complete")
}
queue.async(group: group, execute: {
print("--->callApiGroup1--run task4")
})
}
--->callApiGroup1--run task1
--->callApiGroup1--run task4
--->callApiGroup1--run task2
--->callApiGroup1--run task3
--->callApiGroup1--notify--run complete
示例 2:group 無序調用接口進階版
/// 使用 group 無序調用接口進階版,閉包里是異步方法,需要使用enter、leave方法
func callApiGroup2() {
let group = DispatchGroup()
let queue = DispatchQueue.global()
queue.async(group: group, execute: {
group.enter()
// 模擬網絡請求
queue.asyncAfter(deadline: .now() + 4) {
print("--->callApiGroup2--run task1")
// 通知 group,任務成功完成,要移除,與 enter 成對出現。
group.leave()
}
})
queue.async(group: group, execute: {
group.enter()
// 模擬網絡請求
queue.asyncAfter(deadline: .now() + 1) {
print("--->callApiGroup2--run task2")
// 通知 group,任務成功完成,要移除,與 enter 成對出現。
group.leave()
}
})
queue.async(group: group, execute: {
group.enter()
// 模擬網絡請求
queue.asyncAfter(deadline: .now() + 6) {
print("--->callApiGroup2--run task3")
// 通知 group,任務成功完成,要移除,與 enter 成對出現。
group.leave()
}
})
queue.async(group: group, execute: {
group.enter()
// 模擬網絡請求
queue.asyncAfter(deadline: .now() + 10) {
print("--->callApiGroup2--run task4")
// 通知 group,任務成功完成,要移除,與 enter 成對出現。
group.leave()
}
})
queue.async(group: group, execute: {
group.enter()
print("--->callApiGroup2--run task5")
group.leave()
})
group.notify(queue: queue) {
print("--->callApiGroup2--notify--run complete")
}
}
輸出:
--->callApiGroup2--run task5
--->callApiGroup2--run task2
--->callApiGroup2--run task1
--->callApiGroup2--run task3
--->callApiGroup2--run task4
--->callApiGroup2--notify--run complete