淺談GCD 信號(hào)量dispatch_semaphore的理解及實(shí)際運(yùn)用

關(guān)于信號(hào)量
信號(hào)量:就是一種可用來(lái)控制訪問(wèn)資源的數(shù)量的標(biāo)識(shí),設(shè)定了一個(gè)信號(hào)量享甸,在線程訪問(wèn)之前截碴,加上信號(hào)量的處理,則可告知系統(tǒng)按照我們指定的信號(hào)量數(shù)量來(lái)執(zhí)行多個(gè)線程蛉威。
其實(shí)日丹,這有點(diǎn)類似鎖機(jī)制了,只不過(guò)信號(hào)量都是系統(tǒng)幫助我們處理了蚯嫌,我們只需要在執(zhí)行線程之前哲虾,設(shè)定一個(gè)信號(hào)量值丙躏,并且在使用時(shí),加上信號(hào)量處理方法就行了束凑。
信號(hào)量為0則阻塞線程晒旅,大于0則不會(huì)阻塞。因此我們可以通過(guò)改變信號(hào)量的值湘今,來(lái)控制是否阻塞線程敢朱,從而達(dá)到線程同步。
在GCD中有三個(gè)函數(shù)是semaphore的操作摩瞎,分別是:
  dispatch_semaphore_create   創(chuàng)建一個(gè)semaphore
  dispatch_semaphore_signal   發(fā)送一個(gè)信號(hào)
  dispatch_semaphore_wait    等待信號(hào)
  簡(jiǎn)單的介紹一下這三個(gè)函數(shù)拴签,第一個(gè)函數(shù)有一個(gè)整形的參數(shù),我們可以理解為信號(hào)的總量旗们,dispatch_semaphore_signal是發(fā)送一個(gè)信號(hào)蚓哩,自然會(huì)讓信號(hào)總量加1,dispatch_semaphore_wait等待信號(hào)上渴,當(dāng)信號(hào)總量少于0的時(shí)候就會(huì)一直等待岸梨,否則就可以正常的執(zhí)行,并讓信號(hào)總量-1稠氮,根據(jù)這樣的原理曹阔,我們便可以快速的創(chuàng)建一個(gè)并發(fā)控制來(lái)同步任務(wù)和有限資源訪問(wèn)控制。
實(shí)際開發(fā)中隔披,我們通常會(huì)遇到如下問(wèn)題:
一赃份、某界面存在多個(gè)請(qǐng)求,希望所有請(qǐng)求均結(jié)束才進(jìn)行某操作奢米。
對(duì)于這一問(wèn)題的解決方案很容易想到通過(guò)線程組進(jìn)行實(shí)現(xiàn)抓韩,代碼如下:

dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請(qǐng)求1
        [self request_A];
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請(qǐng)求2
        [self request_B];
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請(qǐng)求3
         [self request_C];
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //界面刷新
        NSLog(@"任務(wù)均完成,刷新界面");
    });

這里我們書寫一個(gè)網(wǎng)絡(luò)請(qǐng)求通用方法鬓长,假設(shè)同時(shí)請(qǐng)求某新聞列表的3頁(yè)數(shù)據(jù)谒拴,每頁(yè)均為一個(gè)獨(dú)立的網(wǎng)絡(luò)請(qǐng)求。使用我們最常用的AFNet請(qǐng)求涉波,方法如下(真實(shí)開發(fā)中可能為banner數(shù)據(jù)請(qǐng)求英上、主體網(wǎng)絡(luò)請(qǐng)求、廣告網(wǎng)絡(luò)請(qǐng)求等):

- (void)request_A {
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];;
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    NSDictionary *parameter = @{@"token":@"63104AB32427EBF89B957BBD1A5C5C11",
                                 @"page":@"1",
                                 @"upTime":@"desc"};
 
    [manager POST:URL parameters:parameter progress:^(NSProgress * _Nonnull uploadProgress) {
 
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
 
        NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
        for (NSDictionary *rowsDict in dict[@"rows"]) {
            NSLog(@"A___%@",rowsDict[@"title"]);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
 
    }];
}

request_B啤覆、request_C分別為請(qǐng)求第二頁(yè)與第三頁(yè)數(shù)據(jù)善延,這里不重復(fù)書寫。為了顯示更加明顯城侧,在請(qǐng)求中打印了對(duì)應(yīng)新聞的標(biāo)題內(nèi)容易遣。

任務(wù)均完成,刷新界面
C___搞笑嫌佑,不是認(rèn)真的
C___攝影 | 街拍
C___生活小竅門
C___傳統(tǒng)中國(guó)
C___想吃的美食系列
B___時(shí)間的見證者
B___沒事 來(lái)吐槽吧……
B___觸動(dòng)心靈的攝影
B___攝影 | 黑白印記
B___每日插畫推薦
A___左愛情豆茫,右面包
A___潮我看
A___世界各國(guó)的人們?cè)趺催^(guò)情人節(jié)
A___一點(diǎn)創(chuàng)意點(diǎn)亮生活
A___攝影 | 隨手拍

運(yùn)行后馬上接收到了線程組完成的提示侨歉,之后數(shù)據(jù)才依次請(qǐng)求下來(lái),很明顯三個(gè)單純的AFNet請(qǐng)求已經(jīng)不能滿足我們的需求了揩魂。線程組完成時(shí)并沒有在我們希望的時(shí)候給予通知幽邓。在真實(shí)開發(fā)中會(huì)造成的問(wèn)題為多個(gè)請(qǐng)求均加載完成,但界面已在未得到數(shù)據(jù)前提前刷新導(dǎo)致界面空白火脉。

因此對(duì)于這種問(wèn)題需要另辟蹊徑牵舵,這里我們就要借助GCD中的信號(hào)量dispatch_semaphore進(jìn)行實(shí)現(xiàn),即營(yíng)造線程同步情況倦挂。
dispatch_semaphore信號(hào)量為基于計(jì)數(shù)器的一種多線程同步機(jī)制畸颅。用于解決在多個(gè)線程訪問(wèn)共有資源時(shí)候,會(huì)因?yàn)槎嗑€程的特性而引發(fā)數(shù)據(jù)出錯(cuò)的問(wèn)題方援。

如果semaphore計(jì)數(shù)大于等于1没炒,計(jì)數(shù)-1,返回犯戏,程序繼續(xù)運(yùn)行送火。如果計(jì)數(shù)為0,則等待先匪。

dispatch_semaphore_signal(semaphore)為計(jì)數(shù)+1操作种吸。dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)為設(shè)置等待時(shí)間,這里設(shè)置的等待時(shí)間是一直等待呀非。
我們將網(wǎng)絡(luò)請(qǐng)求通用方法進(jìn)行修改如下:

- (void)request_A {
    //創(chuàng)建信號(hào)量并設(shè)置計(jì)數(shù)默認(rèn)為0
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];;
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    NSDictionary *parameter = @{@"token":@"63104AB32427EBF89B957BBD1A5C5C11",
                                 @"page":@"1",
                                 @"upTime":@"desc"};
 
    [manager POST:URL parameters:parameter progress:^(NSProgress * _Nonnull uploadProgress) {
 
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //計(jì)數(shù)+1操作
        dispatch_semaphore_signal(sema);
        NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
        for (NSDictionary *rowsDict in dict[@"rows"]) {
            NSLog(@"A___%@",rowsDict[@"title"]);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        ////計(jì)數(shù)+1操作
        dispatch_semaphore_signal(sema);
    }];
    //若計(jì)數(shù)為0則一直等待
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

為方便閱讀坚俗,偽代碼如下:

dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[網(wǎng)絡(luò)請(qǐng)求:{
        成功:dispatch_semaphore_signal(sema);
        失敗:dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

這時(shí)我們?cè)龠\(yùn)行程序姜钳,打印如下:

C___聆聽的耳朵
C___家居 | 你的家里還缺點(diǎn)什么坦冠?
C___logo設(shè)計(jì)
C___時(shí)裝
C___搞笑形耗,不是認(rèn)真的
A___左愛情哥桥,右面包
A___潮我看
A___世界各國(guó)的人們?cè)趺催^(guò)情人節(jié)
A___一點(diǎn)創(chuàng)意點(diǎn)亮生活
A___攝影 | 隨手拍
B___時(shí)間的見證者
B___沒事 來(lái)吐槽吧……
B___觸動(dòng)心靈的攝影
B___攝影 | 黑白印記
B___每日插畫推薦
任務(wù)均完成,刷新界面

運(yùn)行打印可見激涤,通過(guò)信號(hào)量dispatch_semaphore完美的解決了此問(wèn)題拟糕,并且網(wǎng)絡(luò)請(qǐng)求仍為異步,不會(huì)堵塞當(dāng)前主線程倦踢。
二送滞、某界面存在多個(gè)請(qǐng)求,希望請(qǐng)求依次執(zhí)行辱挥。

對(duì)于這個(gè)問(wèn)題通常會(huì)通過(guò)線程依賴進(jìn)行解決犁嗅,因使用GCD設(shè)置線程依賴比較繁瑣,這里通過(guò)NSOperationQueue進(jìn)行實(shí)現(xiàn)晤碘,這里采用比較經(jīng)典的例子褂微,三個(gè)任務(wù)分別為下載圖片功蜓,打水印和上傳圖片,三個(gè)任務(wù)需異步執(zhí)行但需要順序性宠蚂。代碼如下式撼,下載圖片、打水印求厕、上傳圖片仍模擬為分別請(qǐng)求新聞列表3頁(yè)數(shù)據(jù)著隆。

//1.任務(wù)一:下載圖片
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        [self request_A];
    }];
 
    //2.任務(wù)二:打水印
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        [self request_B];
    }];
 
    //3.任務(wù)三:上傳圖片
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        [self request_C];
    }];
 
    //4.設(shè)置依賴
    [operation2 addDependency:operation1];      //任務(wù)二依賴任務(wù)一
    [operation3 addDependency:operation2];      //任務(wù)三依賴任務(wù)二
 
    //5.創(chuàng)建隊(duì)列并加入任務(wù)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

首先我們使用未添加信號(hào)量dispatch_semaphore時(shí)運(yùn)行,打印如下:

B___時(shí)間的見證者
B___沒事 來(lái)吐槽吧……
B___觸動(dòng)心靈的攝影
B___攝影 | 黑白印記
B___每日插畫推薦
A___潮我看
A___左愛情呀癣,右面包
A___世界各國(guó)的人們?cè)趺催^(guò)情人節(jié)
A___一點(diǎn)創(chuàng)意點(diǎn)亮生活
A___攝影 | 隨手拍
C___盤
C___聆聽的耳朵
C___家居 | 你的家里還缺點(diǎn)什么美浦?
C___logo設(shè)計(jì)
C___時(shí)裝

根據(jù)打印結(jié)果可見,若不對(duì)請(qǐng)求方法做處理十艾,其運(yùn)行結(jié)果并不是我們想要的抵代,聯(lián)系實(shí)際需求,A忘嫉、B荤牍、C請(qǐng)求分別對(duì)應(yīng)下載圖片、打水印庆冕、上傳圖片康吵,而此時(shí)運(yùn)行順序則為B->A->C,在未獲得圖片時(shí)即執(zhí)行打水印操作明顯是錯(cuò)誤的访递。重復(fù)運(yùn)行亦會(huì)出現(xiàn)不同結(jié)果晦嵌,即請(qǐng)求不做處理,其結(jié)果不可控?zé)o法預(yù)測(cè)拷姿。線程依賴設(shè)置并未起到作用惭载。

解決此問(wèn)題的方法仍可通過(guò)信號(hào)量dispatch_semaphore進(jìn)行解決。我們將請(qǐng)求方法替換為添加dispatch_semaphore限制的形式响巢。即

dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[網(wǎng)絡(luò)請(qǐng)求:{
        成功:dispatch_semaphore_signal(sema);
        失斆杼稀:dispatch_semaphore_signal(sema);
}]
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

再次重復(fù)運(yùn)行,我們會(huì)發(fā)現(xiàn)每次運(yùn)行結(jié)果均一致踪古,A含长、B、C三任務(wù)異步順序執(zhí)行(A->B->C)

A___潮我看
A___左愛情伏穆,右面包
A___世界各國(guó)的人們?cè)趺催^(guò)情人節(jié)
A___一點(diǎn)創(chuàng)意點(diǎn)亮生活
A___攝影 | 隨手拍
B___時(shí)間的見證者
B___沒事 來(lái)吐槽吧……
B___觸動(dòng)心靈的攝影
B___攝影 | 黑白印記
B___每日插畫推薦
C___盤
C___聆聽的耳朵
C___家居 | 你的家里還缺點(diǎn)什么奖恰?
C___logo設(shè)計(jì)
C___時(shí)裝

通過(guò)重復(fù)運(yùn)行打印結(jié)果可證實(shí)確實(shí)實(shí)現(xiàn)了我們想要的效果挺益。這樣即解決了所提出的問(wèn)題二里覆。

這些就是我對(duì)GCD信號(hào)量的一點(diǎn)點(diǎn)理解畜埋,希望能夠?qū)Υ蠹矣袔椭??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诗鸭,更是在濱河造成了極大的恐慌商叹,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件只泼,死亡現(xiàn)場(chǎng)離奇詭異剖笙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)请唱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門弥咪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人十绑,你說(shuō)我怎么就攤上這事聚至。” “怎么了本橙?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵扳躬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我甚亭,道長(zhǎng)贷币,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任亏狰,我火速辦了婚禮役纹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘暇唾。我一直安慰自己促脉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布策州。 她就那樣靜靜地躺著瘸味,像睡著了一般。 火紅的嫁衣襯著肌膚如雪够挂。 梳的紋絲不亂的頭發(fā)上旁仿,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音下硕,去河邊找鬼丁逝。 笑死汁胆,一個(gè)胖子當(dāng)著我的面吹牛梭姓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嫩码,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼誉尖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了铸题?” 一聲冷哼從身側(cè)響起铡恕,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤琢感,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后探熔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驹针,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年诀艰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柬甥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡其垄,死狀恐怖苛蒲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绿满,我是刑警寧澤臂外,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站喇颁,受9級(jí)特大地震影響漏健,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜橘霎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一漾肮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茎毁,春花似錦克懊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至橡卤,卻和暖如春扮念,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碧库。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工柜与, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嵌灰。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓弄匕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親沽瞭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迁匠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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