有的時候我們會遇到這樣的需求:
循環(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