iOS GCD信號量dispatch_semaphore_t

級別:★☆☆☆☆
標籤:「GCD」「dispatch」「semaphore」
做者: 647
審校: QiShare團隊php


前言:
在研究《iOS 性能監控(二)—— 主線程卡頓監控》中,
發現有一些GCD信號量的知識以前沒有好好梳理過。
故本篇用來梳理一下GCD中信號量dispatch_semaphore_t相關的知識。
git


1、信號量(Semaphore)簡介

信號量(Semaphore)是多線程環境下的一種保護設施,能夠用來保證兩個或多個關鍵代碼不被併發調用。github

在進入一個關鍵代碼段以前,線程必須獲取一個信號量。一旦執行完畢,該線程就會釋放信號量。等待下一個信號量被髮送,線程才能繼續獲取到新信號量並再次執行關鍵代碼段。web

  • 要求:線程進入關鍵代碼段前,必需要獲取到一個信號量。(發信號signal與等信號wait應該要一一對應)
  • 做用:保證關鍵代碼段不被併發調用。

舉個例子: 一個停車場,只能容下5輛車。這時候,來了6輛車。只有前5輛能進去。第6輛車等待,當有一輛車離開停車場時,才能進入。 這裏, 想進停車場 —— 建立信號, 當前有車位 ,領卡進場 —— 發信號, 當前無車位,排隊等卡 —— 等信號, 離開停車場 —— 銷燬信號。objective-c

一般來講,信號量有4種操做。微信

  1. 初始化信號(initialize/create
  2. 發信號(signal/post
  3. 等信號(wait/suspend
  4. 釋放信號(destroy

2、GCD信號量(dispatch_semphore_t)

而在咱們iOS開發中,想使用信號量,首先想到的就是GCD中的dispatch_semphore_t多線程

1. 建立信號量

  • 方法:dispatch_semaphore_create(long value)
dispatch_semaphore_create(long value); //!< 建立信號量
複製代碼
  • 說明:
參數 說明
value 信號量的初始數量(>=0)。
注意:傳遞一個小於零的值將會返回NULL。

若是 value > 0,就至關於建立了個信號量,並同時發出value個信號。 若是 value = 0,就至關於單純僅僅建立了個信號量,還沒發信號。 若是 value < 0,直接failure,返回一個NULL。併發

2. 發送信號量

  • 方法:dispatch_semaphore_signal(dispatch_semaphore_t dsema);
dispatch_semaphore_signal(dispatch_semaphore_t dsema); //!< 發送信號量
複製代碼
  • 說明:
參數 說明
dispatch_semaphore_t 傳入所要發送信號的信號量。
dispatch_semaphore_t的信號計數+1。

3. 等待信號量

  • 方法:dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); //!< 等待信號量
複製代碼
  • 說明:
參數 說明
dispatch_semaphore_t 傳入所要等待信號的信號量。
dispatch_semaphore_t的信號計數-1。
dispatch_time_t 超時等待時間。超過該時間就返回非0,並會直接往下執行。
也能夠設置爲DISPATCH_TIME_FOREVER,永久等待。
返回值 說明
Int 成功收到信號返回0,超時未收到返回非0。

3、信號量的應用

使用信號量使「異步」線程完成「同步」操做。異步

即便是在多線程併發的場景,也能夠經過控制信號量來保證操做的同步。async

舉個例子:一般,咱們要實現異步線程完成同步操做。有兩種作法:

1. 第一種:使用串行隊列+異步操做。

這種狀況只會開啓一條子線程,並按順序執行串行操做。

dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"111:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"222:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"333:%@",[NSThread currentThread]);
    });
複製代碼

這種方式有些缺陷:

第一: 由於是異步操做,因此會開啓一個新的子線程, 同時又是串行隊列,因此只會開啓一條子線程進行同步操做。 喪失了多線程的優點。

第二: 須要寫在一個方法裏去作, 而實際開發中,可能異步分佈在各個方法中,但同時又想串行去執行。

2. 第二種:使用信號量,控制多線程下的同步操做。

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"任務1:%@",[NSThread currentThread]);
        dispatch_semaphore_signal(sem);
    });
    
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務2:%@",[NSThread currentThread]);
        dispatch_semaphore_signal(sem);
    });
    
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務3:%@",[NSThread currentThread]);
    });
複製代碼

固然,這裏只是個例子。在實際應用中, 發送信號(signal),與等待信號(wait)每每是成對出現的。 同時,一般是分開在不一樣的方法裏調用。

例如,在《iOS 性能監控(二)—— 主線程卡頓監控》當中: 監控主線程的CommonModes發生變化時,會發送信號。 同時會開啓一條子線程的loop持續監聽CommonModes的變化,等待信號。 在某些條件下,超時等待時,就說明主線程當前處於卡頓狀態。 保存當前的主線程方法調用堆棧就達到了監控的目的。

PS:詳細的實現,可在QiLagMonitor源碼中查看。


小編微信:可加並拉入《QiShare技術交流羣》。

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
Swift 5.1 (10) - 屬性
iOS App後臺保活
Swift 中使用 CGAffineTransform
iOS 性能監控(一)—— CPU功耗監控
iOS 性能監控(二)—— 主線程卡頓監控
iOS 性能監控(三)—— 方法耗時監控
初識Flutter web
用SwiftUI給視圖添加動畫
用SwiftUI寫一個簡單頁面
iOS App啓動優化(三)—— 本身作一個工具監控App的啓動耗時
iOS App啓動優化(二)—— 使用「Time Profiler」工具監控App的啓動耗時
iOS App啓動優化(一)—— 瞭解App的啓動流程
奇舞週刊

相關文章
相關標籤/搜索