iOS 信號量和 Group 的日常使用案例

信號量

定義

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容