大多數的API 都會需要用closure來異步接收回傳的結果,避免網路的等待擋住整個執行緒的處理。

但在一些情境下,我們可能會希望這個執行緒是等待的,我們希望拿到結果後再繼續往下走。

在這樣的情況下,我們可以使用DispatchSemaphore。

DispatchSemaphore是Swift中的一個簡單而有效的等待機制使用工具。在基本概念上,它讓你可以設置一個可以控制執行程序繼續或停止的信號。很適合當你需要執行接下來的作業之前,需要先確保某些準備工作已經先完成。

DispatchSemaphore的使用基本步驟

  1. 創建一個Semaphore對象

    使用DispatchSemaphore(value: Int)創建一個Semaphore。這個value可用來決定可以同時執行的執行程序數量,通常設為1來保證單一執行。當設為1時,表示只有一個執行程序可以繼續執行,這通常用於保證某段代碼的互斥執行(即同一時間只有一個執行緒能夠進入這段代碼)。

value設為大於1時,意味著允許多個執行程序同時執行,Semaphore會根據這個value來判斷有多少個執行程序可以同時進入臨界區域。例如,設為3時,最多允許三個執行緒同時執行,而超過這個數量的執行緒會被阻塞直到其他執行緒調用signal()釋放資源。

而設為0則意味著初始狀態下所有的執行程序都必須等待,直到有其他程序發出signal()信號。

let semaphore = DispatchSemaphore(value: 0)
  1. 使用wait()停止執行程序

    wait()會停止執行程序,直到有人使用signal()通知它繼續。在等待的時候,執行程序會被阻塞,不會繼續下作,這就讓你能安全的在記錄後段的計算。

    通常會在設置semaphore與wait之間,放入會產生異步執行的程式碼。

    semaphore.wait()
    
  2. 使用signal()取消阻塞

    signal()使執行程序繼續。你可以在完成了準備工作後,使用signal()通知Semaphore,使待機的執行程序繼續下作。

    通常會在異步執行的程式碼完成時呼叫。

    semaphore.signal()
    

實際使用情境

以下是一個簡單的實例,如果你有一個組合要在一個主執行程序中同步執行,但這些作業的執行需要等待下來,你就可以使用DispatchSemaphore來做到。

以下的範例,我們使用一個Semaphore來管理一個非常等待的網路請求。

let semaphore = DispatchSemaphore(value: 0)

func fetchDataFromServer(completion: @escaping () -> Void) {
    DispatchQueue.global().async {
        print("開始請求網路資料...")
        sleep(2)  // 演示網路請求的等待時間
        print("網路資料已請求完畢")
        semaphore.signal()  // 請求完成,發信號通知
    }
}

fetchDataFromServer()  // 起始網路請求
semaphore.wait()  // 等待網路請求完成
print("完成之後繼續執行")

在這個範例中,我們使用Semaphore來停止主執行程序,以便等待網路請求完成後繼續執行。這一方法很有用,當你需要一些項目準備好以後,才可以繼續進行下一步。

使用注意事項

使用DispatchSemaphore時,下面這些是需要特別注意的地方:

  1. 阻塞執行程序的風險: 使用Semaphore需要對阻塞的執行程序有正確的預期,因為如果有程序沒有發出signal(),那麼停止的執行程序就會較難繼續。