iOS使用dispatch_group實現(xiàn)分組并發(fā)網(wǎng)絡(luò)請求

前言

在實際開發(fā)中我們通常會遇到這樣一種需求:某個頁面加載時通過網(wǎng)絡(luò)請求獲得相應(yīng)的數(shù)據(jù)疼鸟,再做某些操作爵政。有時候加載的內(nèi)容需要通過好幾個請求的數(shù)據(jù)組合而成误趴,比如有兩個請求A和B,我們通常為了省事路翻,會將B請求放在A請求成功的回調(diào)中發(fā)起,在B的成功回調(diào)中將數(shù)據(jù)組合起來茄靠,這樣做有明顯的問題:

  • 請求如果多了茂契,需要寫許多嵌套的請求
  • 如果在除了最后一個請求前的某個請求失敗了,就不會執(zhí)行后面的請求嘹黔,數(shù)據(jù)無法加載
  • 請求變成同步的账嚎,這是最大的問題,在網(wǎng)絡(luò)差的情況下儡蔓,如果有n個請求郭蕉,意味著用戶要等待n倍于并發(fā)請求的時間才能看到內(nèi)容

一、某界面存在多個請求喂江,希望所有請求均結(jié)束才進(jìn)行某操作召锈。

同步請求這么low的方式當(dāng)然是不可接受的,所以我們要并發(fā)這些請求获询,在所有請求都執(zhí)行完成功回調(diào)后涨岁,再做加載內(nèi)容或其他操作,考慮再三吉嚣,選擇用GCD的dispatch_group梢薪。

dispatch_group通常有兩種用法,一種是

  • dispatch_group_async(<#dispatch_group_t group#>, <#dispatch_queue_t queue#>, <#^(void)block#>)

創(chuàng)建一個dispatch_group_t尝哆, 將并發(fā)的操作放在block中秉撇,在

dispatch_group_notify(<#dispatch_group_t group#>, <#dispatch_queue_t queue#>, <#^(void)block#>)

的block中執(zhí)行多組block執(zhí)行完畢后的操作,對于網(wǎng)絡(luò)請求來說,在請求發(fā)出時他就算執(zhí)行完畢了琐馆,并不會等待回調(diào)规阀,所以不滿足我們的需求。

dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求1
        NSLog(@"Request_1");
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求2
        NSLog(@"Request_2");
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求3
        NSLog(@"Request_3");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //界面刷新
        NSLog(@"任務(wù)均完成瘦麸,刷新界面");
    });

打印如下

Request_2
Request_1
Request_3
任務(wù)均完成谁撼,刷新界面

網(wǎng)絡(luò)請求我們一般都用異步的,并不知道什么時候是否完成了滋饲。

  • 所以采用另一種用法:

使用dispatch_group_enter(group)和dispatch_group_leave(group)厉碟,這種方式使用更為靈活,enter和leave必須配合使用了赌,有幾次enter就要有幾次leave墨榄,否則group會一直存在。當(dāng)所有enter的block都leave后勿她,會執(zhí)行dispatch_group_notify的block袄秩。

我們當(dāng)然可以在網(wǎng)絡(luò)請求前enter,在執(zhí)行完每個請求的成功回調(diào)后leave逢并,再在notify中執(zhí)行內(nèi)容加載之剧,這樣看來問題就解決了,就像這樣:

dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求1
        [網(wǎng)絡(luò)請求:{
        成功:dispatch_group_leave(group);
        失斂沉摹:dispatch_group_leave(group);
}];
    });
    dispatch_group_enter;
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求2
        [網(wǎng)絡(luò)請求:{
        成功:dispatch_group_leave;
        失敱臣凇:dispatch_group_leave;
}];
    });
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求3
        [網(wǎng)絡(luò)請求:{
        成功:dispatch_group_leave(group);
        失敗:dispatch_group_leave(group);
}];
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //界面刷新
        NSLog(@"任務(wù)均完成玻蝌,刷新界面");
    });
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

打印如下

Request_2
Request_1
Request_3
任務(wù)均完成蟹肘,刷新界面

二、某界面存在多個請求俯树,希望請求依次執(zhí)行帘腹。

對于這個問題通常會通過線程依賴進(jìn)行解決,因使用GCD設(shè)置線程依賴比較繁瑣许饿,這里通過NSOperationQueue進(jìn)行實現(xiàn)阳欲,這里采用比較經(jīng)典的例子,三個任務(wù)分別為下載圖片陋率,打水印和上傳圖片球化,三個任務(wù)需異步執(zhí)行但需要順序性。代碼如下瓦糟,下載圖片筒愚、打水印、上傳圖片仍模擬為分別請求新聞列表3頁數(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)建隊列并加入任務(wù)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

首先我們使用未添加信號量dispatch_semaphore時運行锨能,打印如下

B___圖片打水印
B___圖片打水印
B___圖片打水印
B___圖片打水印
B___圖片打水印
A___下載圖片
A___下載圖片
A___下載圖片
A___下載圖片
A___下載圖片
C___上傳打好水印的圖片
C___上傳打好水印的圖片
C___上傳打好水印的圖片
C___上傳打好水印的圖片
C___上傳打好水印的圖片

根據(jù)打印結(jié)果可見扯再,若不對請求方法做處理,其運行結(jié)果并不是我們想要的址遇,聯(lián)系實際需求,A斋竞、B倔约、C請求分別對應(yīng)下載圖片、打水印坝初、上傳圖片浸剩,而此時運行順序則為B->A->C,在未獲得圖片時即執(zhí)行打水印操作明顯是錯誤的鳄袍。重復(fù)運行亦會出現(xiàn)不同結(jié)果绢要,即請求不做處理,其結(jié)果不可控?zé)o法預(yù)測拗小。線程依賴設(shè)置并未起到作用重罪。

解解決此問題的方法仍可通過信號量dispatch_semaphore進(jìn)行解決。我們將請求方法替換為添加dispatch_semaphore限制的形式哀九。
因此對于這種問題需要另辟蹊徑剿配,這里我們就要借助GCD中的信號量dispatch_semaphore進(jìn)行實現(xiàn),即營造線程同步情況阅束。

dispatch_semaphore信號量為基于計數(shù)器的一種多線程同步機制呼胚。用于解決在多個線程訪問共有資源時候,會因為多線程的特性而引發(fā)數(shù)據(jù)出錯的問題息裸。

如果semaphore計數(shù)大于等于1蝇更,計數(shù)-1,返回呼盆,程序繼續(xù)運行年扩。如果計數(shù)為0,則等待宿亡。

dispatch_semaphore_signal(semaphore)為計數(shù)+1操作常遂。dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)為設(shè)置等待時間,這里設(shè)置的等待時間是一直等待挽荠。我們可以通俗的理解為單柜臺排隊點餐克胳,計數(shù)默認(rèn)為0,每當(dāng)有顧客點餐圈匆,計數(shù)+1漠另,點餐結(jié)束-1歸零繼續(xù)等待下一位顧客。比較類似于NSLock跃赚。

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

再次重復(fù)運行性湿,我們會發(fā)現(xiàn)每次運行結(jié)果均一致,A满败、B肤频、C三任務(wù)異步順序執(zhí)行(A->B->C)

A___下載圖片
A___下載圖片
A___下載圖片
A___下載圖片
A___下載圖片
B___圖片打水印
B___圖片打水印
B___圖片打水印
B___圖片打水印
B___圖片打水印
C___上傳打好水印的圖片
C___上傳打好水印的圖片
C___上傳打好水印的圖片
C___上傳打好水印的圖片
C___上傳打好水印的圖片

通過重復(fù)運行打印結(jié)果可證實確實實現(xiàn)了我們想要的效果。這樣即解決了所提出的問題二算墨。

后續(xù)
如果看的不是特別明白宵荒,可以看看這篇文章,寫的很棒
點我進(jìn)入

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末净嘀,一起剝皮案震驚了整個濱河市报咳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挖藏,老刑警劉巖暑刃,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異膜眠,居然都是意外死亡岩臣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門柴底,熙熙樓的掌柜王于貴愁眉苦臉地迎上來婿脸,“玉大人,你說我怎么就攤上這事柄驻『鳎” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵鸿脓,是天一觀的道長抑钟。 經(jīng)常有香客問我,道長野哭,這世上最難降的妖魔是什么在塔? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮拨黔,結(jié)果婚禮上蛔溃,老公的妹妹穿的比我還像新娘。我一直安慰自己篱蝇,他們只是感情好贺待,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著零截,像睡著了一般麸塞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涧衙,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天哪工,我揣著相機與錄音奥此,去河邊找鬼。 笑死雁比,一個胖子當(dāng)著我的面吹牛稚虎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播偎捎,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼祥绞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸭限?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤两踏,失蹤者是張志新(化名)和其女友劉穎败京,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梦染,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡赡麦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帕识。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泛粹。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肮疗,靈堂內(nèi)的尸體忽然破棺而出晶姊,到底是詐尸還是另有隱情,我是刑警寧澤伪货,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布们衙,位于F島的核電站,受9級特大地震影響碱呼,放射性物質(zhì)發(fā)生泄漏蒙挑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一愚臀、第九天 我趴在偏房一處隱蔽的房頂上張望忆蚀。 院中可真熱鬧,春花似錦姑裂、人聲如沸馋袜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桃焕。三九已至,卻和暖如春捧毛,著一層夾襖步出監(jiān)牢的瞬間观堂,已是汗流浹背让网。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留师痕,地道東北人溃睹。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像胰坟,于是被迫代替她去往敵國和親因篇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

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

  • 一. 重點: 1.dispatch_queue_create(生成Dispatch Queue) 2.Main D...
    BestJoker閱讀 1,587評論 2 2
  • 1 GCD 術(shù)語 1.1 Serial vs. Concurrent 串行 vs. 并發(fā) 概念:該術(shù)語描述執(zhí)行當(dāng)前...
    NinthDay閱讀 3,933評論 2 38
  • Managing Units of Work(管理工作單位) 調(diào)度塊允許您直接配置隊列中各個工作單元的屬性笔横。它們還...
    edison0428閱讀 7,992評論 0 1
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了竞滓!去的時候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似吹缔。...
    Dely閱讀 9,243評論 21 42
  • “你應(yīng)該發(fā)現(xiàn)你現(xiàn)在生活中的一切美好商佑、真實的東西∠崽粒回首過去會使你產(chǎn)生競爭的意識茶没,而年齡是無法競爭的......當(dāng)我應(yīng)...
    大大大栗子閱讀 1,162評論 0 1