dispatch_semaphore 會形成優先級反轉,慎用!

1、結論:慎用 dispatch_semaphore 作線程同步

與 OSSpinlock相似,使用 dispatch_semaphore 容易形成優先級反轉:html

  • 此 API 沒有記錄當前持有信號量的線程,因此有高優先級的線程在等待鎖時,內核沒法知道該提升哪一個線程的調試優先級(QoS)markdown

  • 若是鎖持有者優先級比其餘線程低,高優先級的等待線程將一直等待app

高效率的線程同步有兩個關鍵點:異步

  • 不忙等
  • 記錄持有者

自旋鎖是兩點都不符合,dispatch_semaphore 是隻符合不忙等。async

2、原理說明

下面先介紹一下 iOS 平臺上的 QoS 概念和優先級反轉避免機制,最後再說明爲何 dispatch_semaphore 不能避免優先級反轉。ide

1. QoS 傳遞

QoS(Quality of Service),用來指示某任務或隊列的運行優先級。oop

  • iOS 系統主要使用如下兩種機制來在不一樣線程(或 queue)間傳遞 QoS:ui

    • 機制1:dispatch_asyncspa

    • 機制2:基於 XPC 的進程間通訊(IPC)線程

  • 系統的 QoS 傳遞規則比較複雜,主要參考如下信息:

    • 當前線程的 QoS

    • 若是是使用 dispatch_block_create() 方法生成的 dispatch_block,則考慮生成 block 時所調用的參數

    • dispatch_async 或 IPC 的目標 queue 或線程的 QoS

    調度程序會根據這些信息決定 block 以什麼優先級運行。具體用法請參見QoS使用官方文檔

若是沒有其餘線程同步地等待此 block,則 block 就按上面所說的優先級來運行。

若是出現了線程間同步等待的狀況,則調度程序會根據狀況調整線程的運行優先級。

2. 優先級反轉及避免

  • 優先級反轉:當前線程在同步地等待某個線程(線程1)完成某項操做,而當前線程的優先級比線程1的優先級高。

  • 優先級反轉避免機制(Priority inversion avoidance):若是當前線程因等待某線程(線程1)上正在進行的操做(如 block1)而受阻,而系統知道 block1 所在的目標線程(owner),系統會經過提升相關線程的優先級來解決優先級反轉的問題。

    • 若是系統不知道 block1 所在目標線程,則沒法知道應該提升誰的優先級,也就沒法解決反轉問題

    • 記錄了持有者信息(owner)的系統 API 以下:

      • pthread mutex、os_unfair_lock、以及基於這兩者實現的其餘上層 API

        • dispatch_once 的實現是基於 os_unfair_lock 的

        • NSLock、NSRecursiveLock、@synchronized 等的實現是基於 pthread mutex 的

      • dispatch_sync()

      • xpc_connection_send_with_message_sync()

      使用以上這些 API 可以在發生優先級反轉時使系統啓用優先級反轉避免機制。

3. dispatch_semaphroe 不能避免優先級反轉的緣由

  • semaphore 不是一個異步方法,因此它沒有 QoS 的概念

  • 在調用 dispatch_semaphore_wait() 時,系統不知道哪一個線程會調用 dispatch_semaphore_signal() 方法,因此係統沒法知道 owner 信息

dispatch_group 跟 semaphore 相似,在調用 enter() 方法時,沒法預知誰會調用 leave(),因此係統也沒法知道其 owner 是誰

參考資料

相關文章
相關標籤/搜索