信號量Semaphore的典型應用場景

在iOS多線程開發(fā)環(huán)境中墨叛,我們往往會用信號量Semaphore解決一些特別的問題,它不僅高效而且也易于理解尝盼。這里我總結(jié)了加鎖吞滞、異步返回、控制線程并發(fā)數(shù)這三個用途盾沫,下面通過一些例子進行解釋裁赠。

一、加鎖

代碼形式:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 10000; i++) {
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        //臨界區(qū)疮跑,即待加鎖的代碼區(qū)域

        dispatch_semaphore_signal(semaphore);
    });
}

在進行多線程任務之前组贺,首先創(chuàng)建一個計數(shù)為1的信號量,這樣可以保證同一時刻只有一個線程在訪問臨界區(qū)祖娘,dispatch_semaphore_create() 會為我們完成。

在要訪問臨界區(qū)之前,通過 dispatch_semaphore_wait() 函數(shù)渐苏,我們可以在信號量為 0 時掀潮,讓臨界區(qū)外的線程進入等待狀態(tài)。

在這里琼富,當?shù)谝粭l線程訪問臨界區(qū)時仪吧,信號量計數(shù)為初始值1,

dispatch_semaphore_wait() 函數(shù)判斷到計數(shù)大于0鞠眉,于是將計數(shù)減1薯鼠,從而線程允許訪問臨界區(qū)。其它線程因為信號量等于0械蹋,就在臨界區(qū)外等待出皇。

在第一條線程訪問完臨界區(qū)后,這條線程需要發(fā)出一個信號哗戈,來表明我已經(jīng)用完臨界區(qū)的資源了郊艘,下個正在等待的線程可以去訪問了。

dispatch_semaphore_signal()會將信號量計數(shù)加1唯咬,就好像發(fā)出了一個信號一樣纱注,下個在臨界區(qū)前等待的線程會去接收它。接收到了信號的線程判斷到信號量計數(shù)大于零了胆胰,于是訪問臨界區(qū)狞贱。

通過重復這個過程,所有線程都會安全地訪問一遍臨界區(qū)蜀涨。

貼一段YYKit中的簡單的加鎖代碼:

- (instancetype)init {
    self = [super init];
    _lock = dispatch_semaphore_create(1);
    return self;
}

- (NSURL *)imageURL {
    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
    NSURL *imageURL = _imageURL;
    dispatch_semaphore_signal(_lock);
    return imageURL;
}

二瞎嬉、異步任務,同步返回

- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    __block NSArray *tasks = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        //task賦值勉盅,代碼有點長佑颇,就不貼了
        dispatch_semaphore_signal(semaphore);
    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return tasks;
}

上面是 AFNetworking 的一段代碼,我且稱之為異步返回草娜。這段代碼的功能是通過異步的請求取得鍵路徑為 keyPath 的任務數(shù)組 tasks挑胸,然后返回它。這個方法雖然是異步的宰闰,但是執(zhí)行時間較短茬贵。

碰到這種情況,我們肯定最先想到的是用代碼塊 block 或者代理 delegate 來實現(xiàn)移袍,然后我們就得去聲明一個代理解藻,寫一個協(xié)議方法,或者寫一個帶有一個參數(shù)的代碼塊葡盗,這里AFNetworking巧妙地通過信號量解決了螟左。

我們跟之前的加鎖對比,可以發(fā)現(xiàn),信號量在創(chuàng)建時計數(shù)是0胶背,
dispatch_semaphore_signal() 函數(shù)在 dispatch_semaphore_wait() 函數(shù)之前巷嚣。

AFNetworking 把 dispatch_semaphore_wait() 函數(shù)放在返回語句之前,同時信號量計數(shù)初始為0钳吟,是為了讓線程在 tasks 有值之前一直等待廷粒。獲取 tasks 的異步操作結(jié)束之后,這時候 tasks 賦值好了红且,于是通過 dispatch_semaphore_signal() 函數(shù)發(fā)出信號坝茎,外面的線程就知道不用等待,可以返回 tasks 了暇番。

其實信號量進行了隱式的線程間通信嗤放,仔細想想,信號量本身是否線程安全呢奔誓?

三斤吐、控制線程并發(fā)數(shù)

在 GCD 中,dispatch_async() 異步操作可以產(chǎn)生新的線程厨喂,但是方法本身沒辦法限制線程的最大并發(fā)數(shù)和措,線程的創(chuàng)建和銷毀是由 GCD 底層管理的。
了解 NSOperationQueue 的同學肯定知道蜕煌,通過 maxConcurrentOperationCount 屬性可以設置它的最大并發(fā)數(shù)派阱。那么在GCD中,對應的解決方法就是使用信號量斜纪。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
for (int i = 0; i < 1000; ++i) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        //多線程代碼
        
        dispatch_semaphore_signal(semaphore);
    });
}

其實跟加鎖代碼非常相似贫母,區(qū)別在于,在初始化信號量時盒刚,將計數(shù)賦值為最大并發(fā)數(shù)腺劣。在應用場景上,限制線程并發(fā)數(shù)是為了性能考慮因块,而加鎖是為了安全而考慮橘原。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涡上,隨后出現(xiàn)的幾起案子趾断,更是在濱河造成了極大的恐慌,老刑警劉巖吩愧,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芋酌,死亡現(xiàn)場離奇詭異,居然都是意外死亡雁佳,警方通過查閱死者的電腦和手機脐帝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門同云,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腮恩,你說我怎么就攤上這事梢杭∥录妫” “怎么了秸滴?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長募判。 經(jīng)常有香客問我荡含,道長,這世上最難降的妖魔是什么届垫? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任释液,我火速辦了婚禮,結(jié)果婚禮上装处,老公的妹妹穿的比我還像新娘误债。我一直安慰自己,他們只是感情好妄迁,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布寝蹈。 她就那樣靜靜地躺著,像睡著了一般登淘。 火紅的嫁衣襯著肌膚如雪箫老。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天黔州,我揣著相機與錄音耍鬓,去河邊找鬼。 笑死流妻,一個胖子當著我的面吹牛牲蜀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绅这,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涣达,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了君躺?” 一聲冷哼從身側(cè)響起峭判,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棕叫,沒想到半個月后林螃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡俺泣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年疗认,在試婚紗的時候發(fā)現(xiàn)自己被綠了完残。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡横漏,死狀恐怖谨设,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缎浇,我是刑警寧澤扎拣,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站素跺,受9級特大地震影響二蓝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜指厌,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一刊愚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧踩验,春花似錦鸥诽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厕九,卻和暖如春蓖捶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搓劫,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓并闲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谷羞。 傳聞我的和親對象是個殘疾皇子帝火,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,385評論 8 265
  • (第286首 靠主有福歌)4 行在主道中的人真有福, 他日日向前又向上湃缎,因信行走在恩主慈愛中犀填,他所需天父必供養(yǎng)。要...
    不丹丹是閱讀 817評論 0 50
  • 情商高的人比智商高的可怕多了嗓违,一個智商高的人很容易在學校表現(xiàn)出色九巡,最終卻落得平庸的一生。而一個情商特別高的人蹂季,你最...
    離兮憂兮閱讀 3,282評論 1 4
  • 靜坐光陰冕广, 素心已嫻疏日, 然, 一筆婉約不了塵夢撒汉, 一語唯美不了流年沟优, 唯有一顆素心, 一份懂得睬辐, 才能淡塵挠阁, 淡世...
    落墨筆尖惆閱讀 294評論 0 0