iOS dispatch_semaphore信號量的使用(for循環(huán)請求網(wǎng)絡(luò)時棚贾,使用信號量導(dǎo)致死鎖)

有的時候我們會遇到這樣的需求:
循環(huán)請求網(wǎng)絡(luò),但是在循環(huán)的過程中榆综,必須上一個網(wǎng)絡(luò)回調(diào)完成后才能請求下一個網(wǎng)絡(luò)即進(jìn)行下一個循環(huán)妙痹,也就是所謂的多個異步網(wǎng)絡(luò)做同步請求,首先想到的就是用信號量攔截鼻疮,但是發(fā)現(xiàn)AFNetWorking配合信號量使用時怯伊,網(wǎng)絡(luò)不回調(diào)了,是什么原因引起的網(wǎng)絡(luò)無法回調(diào)判沟。下面我們模擬下正常使用過程并分析耿芹,如下:

-(void)semaphoreTest{
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
   for (int i = 0; i<10; i++) {
        [self semaphoreTestBlock:^(NSString *TNT) {
            NSLog(@"任務(wù)完成 %d",i);
            dispatch_semaphore_signal(semaphore);
        }];
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"信號量限制 %d",i);
    }
}

//這里用延遲模擬異步網(wǎng)絡(luò)請求
-(void)semaphoreTestBlock:(void(^)(NSString * TNT))block{
    /*
      queue 的類型無論是串行隊列還是并行隊列并不影響最終結(jié)果
       如果 queue = dispatch_get_main_queue() 將會堵塞組線程,造成死鎖
    */
    dispatch_queue_t queue = dispatch_queue_create("myqueue",DISPATCH_QUEUE_SERIAL);
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
        block(@"完成");
    });
}

這段代碼的輸出結(jié)果為:

2019-10-11 14:40:23.961328+0800 LJC[9013:1358198] 任務(wù)完成 0
2019-10-11 14:40:23.961751+0800 LJC[9013:1356826] 信號量限制 0
2019-10-11 14:40:25.061312+0800 LJC[9013:1358198] 任務(wù)完成 1
2019-10-11 14:40:25.061673+0800 LJC[9013:1356826] 信號量限制 1
2019-10-11 14:40:26.062082+0800 LJC[9013:1356931] 任務(wù)完成 2
2019-10-11 14:40:26.062381+0800 LJC[9013:1356826] 信號量限制 2
2019-10-11 14:40:27.062883+0800 LJC[9013:1356931] 任務(wù)完成 3
2019-10-11 14:40:27.063275+0800 LJC[9013:1356826] 信號量限制 3
2019-10-11 14:40:28.160535+0800 LJC[9013:1356931] 任務(wù)完成 4
2019-10-11 14:40:28.160988+0800 LJC[9013:1356826] 信號量限制 4
2019-10-11 14:40:29.161327+0800 LJC[9013:1356931] 任務(wù)完成 5
2019-10-11 14:40:29.161512+0800 LJC[9013:1356826] 信號量限制 5
2019-10-11 14:40:30.161756+0800 LJC[9013:1356931] 任務(wù)完成 6
2019-10-11 14:40:30.161989+0800 LJC[9013:1356826] 信號量限制 6
2019-10-11 14:40:31.261507+0800 LJC[9013:1356931] 任務(wù)完成 7
2019-10-11 14:40:31.261912+0800 LJC[9013:1356826] 信號量限制 7
2019-10-11 14:40:32.361503+0800 LJC[9013:1356931] 任務(wù)完成 8
2019-10-11 14:40:32.361870+0800 LJC[9013:1356826] 信號量限制 8
2019-10-11 14:40:33.461544+0800 LJC[9013:1358198] 任務(wù)完成 9
2019-10-11 14:40:33.461953+0800 LJC[9013:1356826] 信號量限制 9

如果我們把
dispatch_queue_t queue = dispatch_queue_create("myqueue",DISPATCH_QUEUE_SERIAL);
替換成
dispatch_queue_t queue = dispatch_get_main_queue()
發(fā)現(xiàn)輸出結(jié)果為空

為什么呢挪哄?
首先我們要知道
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
他怎么才能實現(xiàn)鎖的功能吧秕,他的鎖其實是針對線程的,我們當(dāng)前任務(wù)是在主線程執(zhí)行的迹炼,我們就需要在主線程上鎖砸彬。
完成任務(wù)我們?nèi)⑿盘柫?1,即執(zhí)行
dispatch_semaphore_signal(semaphore)
這個時候發(fā)現(xiàn)你的回調(diào)也是在主線程觸發(fā)的,但是此時主線程上鎖斯入,已經(jīng)卡住了砂碉,是不能讓你在主線程做任務(wù)的,這就形成了相互等待,卡死了刻两,所以我們需要將回調(diào)任務(wù)放在非主線程中(以目前這個例子來說增蹭,就是非主線程,其實我們最終調(diào)整的目的是讓執(zhí)行任務(wù)和回調(diào)任務(wù)不在同一線程即可)闹伪。

那我們?nèi)绻麑⑷蝿?wù)(for循環(huán))在子線程中執(zhí)行,回調(diào)在主線程中是否可以呢沪铭?下面我們修改代碼

-(void)semaphoreTest{
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
           
           for (int i = 0; i<10; i++) {
               [self semaphoreTestBlock:^(NSString *TNT) {
                   NSLog(@"任務(wù)完成 %d",i);
                   dispatch_semaphore_signal(semaphore);
               }];
               
               dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
               NSLog(@"信號量限制 %d",i);
           }
    });
}

-(void)semaphoreTestBlock:(void(^)(NSString * TNT))block{
    
//    dispatch_queue_t queue = dispatch_queue_create("myqueue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
        block(@"完成");
    });
}

輸出結(jié)果

2019-10-11 14:51:00.224109+0800 LJC[9063:1362953] 任務(wù)完成 0
2019-10-11 14:51:00.224486+0800 LJC[9063:1363099] 信號量限制 0
2019-10-11 14:51:01.325117+0800 LJC[9063:1362953] 任務(wù)完成 1
2019-10-11 14:51:01.325493+0800 LJC[9063:1363099] 信號量限制 1
2019-10-11 14:51:02.425129+0800 LJC[9063:1362953] 任務(wù)完成 2
2019-10-11 14:51:02.425491+0800 LJC[9063:1363099] 信號量限制 2
2019-10-11 14:51:03.524266+0800 LJC[9063:1362953] 任務(wù)完成 3
2019-10-11 14:51:03.524715+0800 LJC[9063:1363099] 信號量限制 3
2019-10-11 14:51:04.625254+0800 LJC[9063:1362953] 任務(wù)完成 4
2019-10-11 14:51:04.625659+0800 LJC[9063:1363099] 信號量限制 4
2019-10-11 14:51:05.725228+0800 LJC[9063:1362953] 任務(wù)完成 5
2019-10-11 14:51:05.725573+0800 LJC[9063:1363099] 信號量限制 5
2019-10-11 14:51:06.726094+0800 LJC[9063:1362953] 任務(wù)完成 6
2019-10-11 14:51:06.726442+0800 LJC[9063:1363099] 信號量限制 6
2019-10-11 14:51:07.825270+0800 LJC[9063:1362953] 任務(wù)完成 7
2019-10-11 14:51:07.825613+0800 LJC[9063:1363099] 信號量限制 7
2019-10-11 14:51:08.925323+0800 LJC[9063:1362953] 任務(wù)完成 8
2019-10-11 14:51:08.925674+0800 LJC[9063:1363099] 信號量限制 8
2019-10-11 14:51:10.025359+0800 LJC[9063:1362953] 任務(wù)完成 9
2019-10-11 14:51:10.025722+0800 LJC[9063:1363099] 信號量限制 9

這就驗證了我們的想法, 執(zhí)行任務(wù)和任務(wù)回調(diào)是不能在一個線程中的

整理

在使用信號量的時候偏瓤,需要注意 dispatch_semaphore_wait 需要和 任務(wù) 放在同一線程杀怠,在任務(wù)執(zhí)行異步回調(diào)的時候,需要將回調(diào)放在與執(zhí)行任務(wù)不同的線程中厅克,因為如果在同一線程中 dispatch_semaphore_wait 操作會造成相互等待導(dǎo)致死鎖問題赂弓,我們在使用 AFNetWorking 的時候,他默認(rèn)的回調(diào)是在 主線程中盒至,所以我們在配合 AFNetWorking 使用信號量的時候可以指定 AFNetWorking 的回調(diào)線程,或者我們在執(zhí)行任務(wù)的時候窗骑,將任務(wù)放在其他線程

注釋:
寫這篇文章是因為我在用信號量配合AFNetWorking做網(wǎng)路任務(wù)的時候發(fā)現(xiàn)一只卡死,在網(wǎng)上找的都說指定AFNetWorking 的 completionQueue ,然后我更改了代碼漆枚,request是我們網(wǎng)絡(luò)對AFNetWorking的封裝對象實例创译,按理來說是沒問題的,但是不知道為什么還是會造成死鎖墙基。目前原因沒找到软族。所以我將for循環(huán)再放了子線程中

request.sessionManager.completionQueue = dispatch_get_global_queue(0, 0);

如發(fā)現(xiàn)理解錯誤,望指出 ^_^ THANKS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末残制,一起剝皮案震驚了整個濱河市立砸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌初茶,老刑警劉巖颗祝,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異恼布,居然都是意外死亡螺戳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進(jìn)店門桥氏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來温峭,“玉大人,你說我怎么就攤上這事字支》锊兀” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵堕伪,是天一觀的道長揖庄。 經(jīng)常有香客問我,道長欠雌,這世上最難降的妖魔是什么蹄梢? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮富俄,結(jié)果婚禮上禁炒,老公的妹妹穿的比我還像新娘。我一直安慰自己霍比,他們只是感情好幕袱,可當(dāng)我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悠瞬,像睡著了一般们豌。 火紅的嫁衣襯著肌膚如雪涯捻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天望迎,我揣著相機與錄音障癌,去河邊找鬼。 笑死辩尊,一個胖子當(dāng)著我的面吹牛涛浙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摄欲,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼蝗拿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蒿涎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤惦辛,失蹤者是張志新(化名)和其女友劉穎劳秋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胖齐,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡玻淑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了呀伙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片补履。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖剿另,靈堂內(nèi)的尸體忽然破棺而出箫锤,到底是詐尸還是另有隱情,我是刑警寧澤雨女,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布谚攒,位于F島的核電站,受9級特大地震影響氛堕,放射性物質(zhì)發(fā)生泄漏馏臭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一讼稚、第九天 我趴在偏房一處隱蔽的房頂上張望括儒。 院中可真熱鬧,春花似錦锐想、人聲如沸帮寻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽规婆。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抒蚜,已是汗流浹背掘鄙。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嗡髓,地道東北人操漠。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像饿这,于是被迫代替她去往敵國和親浊伙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,587評論 2 350

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