iOS dispatch_semaphore信号量的使用(for循环请求网络时,使用信号量导致死锁)

作者 : 开心源码 本文共4517个字,预计阅读时间需要12分钟 发布时间: 2022-05-13 共206人阅读

有的时候我们会遇到这样的需求:
循环请求网络,但是在循环的过程中,必需上一个网络回调完成后才能请求下一个网络即进行下一个循环,也就是所谓的多个异步网络做同步请求,首先想到的就是用信号量阻拦,但是发现AFNetWorking配合信号量使用时,网络不回调了,是什么起因引起的网络无法回调。下面我们模拟下正常使用过程并分析,如下:

-(void)semaphoreTest{        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);       for (int i = 0; i<10; i++) {        [self semaphoreTestBlock:^(NSString *TNT) {            NSLog(@"任务完成 %d",i);            dispatch_semaphore_signal(semaphore);        }];                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);        NSLog(@"信号量限制 %d",i);    }}//这里用推迟模拟异步网络请求-(void)semaphoreTestBlock:(void(^)(NSString * TNT))block{    /*      queue 的类型无论是串行队列还是并行队列并不影响最终结果       假如 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(@"完成");    });}

这段代码的输出结果为:

2019-10-11 14:40:23.961328+0800 LJC[9013:1358198] 任务完成 02019-10-11 14:40:23.961751+0800 LJC[9013:1356826] 信号量限制 02019-10-11 14:40:25.061312+0800 LJC[9013:1358198] 任务完成 12019-10-11 14:40:25.061673+0800 LJC[9013:1356826] 信号量限制 12019-10-11 14:40:26.062082+0800 LJC[9013:1356931] 任务完成 22019-10-11 14:40:26.062381+0800 LJC[9013:1356826] 信号量限制 22019-10-11 14:40:27.062883+0800 LJC[9013:1356931] 任务完成 32019-10-11 14:40:27.063275+0800 LJC[9013:1356826] 信号量限制 32019-10-11 14:40:28.160535+0800 LJC[9013:1356931] 任务完成 42019-10-11 14:40:28.160988+0800 LJC[9013:1356826] 信号量限制 42019-10-11 14:40:29.161327+0800 LJC[9013:1356931] 任务完成 52019-10-11 14:40:29.161512+0800 LJC[9013:1356826] 信号量限制 52019-10-11 14:40:30.161756+0800 LJC[9013:1356931] 任务完成 62019-10-11 14:40:30.161989+0800 LJC[9013:1356826] 信号量限制 62019-10-11 14:40:31.261507+0800 LJC[9013:1356931] 任务完成 72019-10-11 14:40:31.261912+0800 LJC[9013:1356826] 信号量限制 72019-10-11 14:40:32.361503+0800 LJC[9013:1356931] 任务完成 82019-10-11 14:40:32.361870+0800 LJC[9013:1356826] 信号量限制 82019-10-11 14:40:33.461544+0800 LJC[9013:1358198] 任务完成 92019-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()
发现输出结果为空

为什么呢?
首先我们要知道
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
他怎样才能实现锁的功能,他的锁其实是针对线程的,我们当前任务是在主线程执行的,我们就需要在主线程上锁。
完成任务我们去将信号量+1,即执行
dispatch_semaphore_signal(semaphore)
这个时候发现你的回调也是在主线程触发的,但是此时主线程上锁,已经卡住了,是不能让你在主线程做任务的,这就形成了相互等待,卡死了,所以我们需要将回调任务放在非主线程中(以目前这个例子来说,就是非主线程,其实我们最终调整的目的是让执行任务和回调任务不在同一线程就可)。

那我们假如将任务(for循环)在子线程中执行,回调在主线程中能否可以呢?下面我们修改代码

-(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(@"任务完成 %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(@"完成");    });}

输出结果

2019-10-11 14:51:00.224109+0800 LJC[9063:1362953] 任务完成 02019-10-11 14:51:00.224486+0800 LJC[9063:1363099] 信号量限制 02019-10-11 14:51:01.325117+0800 LJC[9063:1362953] 任务完成 12019-10-11 14:51:01.325493+0800 LJC[9063:1363099] 信号量限制 12019-10-11 14:51:02.425129+0800 LJC[9063:1362953] 任务完成 22019-10-11 14:51:02.425491+0800 LJC[9063:1363099] 信号量限制 22019-10-11 14:51:03.524266+0800 LJC[9063:1362953] 任务完成 32019-10-11 14:51:03.524715+0800 LJC[9063:1363099] 信号量限制 32019-10-11 14:51:04.625254+0800 LJC[9063:1362953] 任务完成 42019-10-11 14:51:04.625659+0800 LJC[9063:1363099] 信号量限制 42019-10-11 14:51:05.725228+0800 LJC[9063:1362953] 任务完成 52019-10-11 14:51:05.725573+0800 LJC[9063:1363099] 信号量限制 52019-10-11 14:51:06.726094+0800 LJC[9063:1362953] 任务完成 62019-10-11 14:51:06.726442+0800 LJC[9063:1363099] 信号量限制 62019-10-11 14:51:07.825270+0800 LJC[9063:1362953] 任务完成 72019-10-11 14:51:07.825613+0800 LJC[9063:1363099] 信号量限制 72019-10-11 14:51:08.925323+0800 LJC[9063:1362953] 任务完成 82019-10-11 14:51:08.925674+0800 LJC[9063:1363099] 信号量限制 82019-10-11 14:51:10.025359+0800 LJC[9063:1362953] 任务完成 92019-10-11 14:51:10.025722+0800 LJC[9063:1363099] 信号量限制 9

这就验证了我们的想法, 执行任务和任务回调是不能在一个线程中的

整理

在使用信号量的时候,需要注意 dispatch_semaphore_wait 需要和 任务 放在同一线程,在任务执行异步回调的时候,需要将回调放在与执行任务不同的线程中,由于假如在同一线程中 dispatch_semaphore_wait 操作会造成相互等待导致死锁问题,我们在使用 AFNetWorking 的时候,他默认的回调是在 主线程中,所以我们在配合 AFNetWorking 使用信号量的时候可以指定 AFNetWorking 的回调线程,或者者我们在执行任务的时候,将任务放在其余线程

注释:
写这篇文章是由于我在用信号量配合AFNetWorking做网路任务的时候发现一只卡死,在网上找的都说指定AFNetWorking 的 completionQueue ,而后我更改了代码,request是我们网络对AFNetWorking的封装对象实例,按理来说是没问题的,但是不知道为什么还是会造成死锁。目前起因没找到。所以我将for循环再放了子线程中

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

如发现了解错误,望指出 ^_^ THANKS

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » iOS dispatch_semaphore信号量的使用(for循环请求网络时,使用信号量导致死锁)

发表回复