我们都知道线程同步方案中dispatch_semaphore
是一个很不错的选择,但是dispatch_semaphore
如果在主线程中等待,另外开启线程再回到主线程释放信号,就会出现信号卡死的情况。
简单回顾一下信号量的使用,API如下:
// 创建信号量,参数:信号量的初值,表示最多几个资源可访问。
dispatch_semaphore_create(信号量值)
//等待信号量
dispatch_semaphore_wait(信号量,等待时间)
//发送信号量
dispatch_semaphore_signal(信号量)
1、执行dispatch_semaphore_create 会根据传入的long型参数创建对应数目的信号量;这个也是可以控制并发线程的数量;
2、执行dispatch_semaphore_signal 会增加一个信号量;
3、执行dispatch_semaphore_wait ,如果信号量<=0, 就行进入休眠等待;如果> 0, 则会减少一个信号量, 并执行后面的代码;
4、正常的使用顺序是先wait然后再signal,这两个函数通常成对使用。
5、如果信号量是0,就会根据传入的等待时间来等待。
- (void)viewDidLoad {
[super viewDidLoad];
if ([self testSemaphore]) {
NSLog(@"执行了信号量的释放操作");
}else {
NSLog(@"未执行信号量的释放操作");
}
}
- (BOOL)testSemaphore {
__block BOOL resultValue = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"创建信号量");
[self semaphoreBlock:^{
resultValue = YES;
NSLog(@"开始释放信号量,%@", [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
NSLog(@"结束释放信号量,%@", [NSThread currentThread]);
}];
NSLog(@"等待信号量,%@", [NSThread currentThread]);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行returen操作啦");
return resultValue;
}
- (void)semaphoreBlock:(void(^)(void))block {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"进来block了,%@", [NSThread currentThread]);
block();
});
}
就以上一份代码为例,我们修改一下,把semaphoreBlock:(void(^)(void))block
这个方法里面的dispatch_get_global_queue
替换成dispatch_get_main_queue
的话,那么线程就会卡死,代码如下:
- (void)semaphoreBlock:(void(^)(void))block {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"进来block了,%@", [NSThread currentThread]);
block();
});
}
执行代码查看结果如下:
执行的结果可以看出,线程被卡死了~~
主线程阻塞,直到收到信号才会往下继续执行
;开启了另外一个线程后回到主线程中执行
,由于此时主线程是阻塞的,那么dispatch_semaphore_signal(s)不会执行,这形成了死锁的情况。testSemaphore
任务的后面;但是在主线程时串行队列,需要先执行完testSemaphore
这个任务,才能执行dispatch_semaphore_signal(s)这个任务;而要执行testSemaphore
这个任务,又需要执行dispatch_semaphore_signal(s)这个任务; 所以两个任务相互等待,就死锁了)。要么把整个任务都放到子线程中,要么信号量的释放不在主线程中释放
。代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if ([self testSemaphore]) {
NSLog(@"执行了信号量的释放操作");
}else {
NSLog(@"未执行信号量的释放操作");
}
});
}
- (BOOL)testSemaphore {
__block BOOL resultValue = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"创建信号量");
[self semaphoreBlock:^{
resultValue = YES;
NSLog(@"开始释放信号量,%@", [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
NSLog(@"结束释放信号量,%@", [NSThread currentThread]);
}];
NSLog(@"等待信号量,%@", [NSThread currentThread]);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行returen操作啦");
return resultValue;
}
- (void)semaphoreBlock:(void(^)(void))block {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"进来block了,%@", [NSThread currentThread]);
block();
});
}
代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
if ([self testSemaphore]) {
NSLog(@"执行了信号量的释放操作");
}else {
NSLog(@"未执行信号量的释放操作");
}
}
- (BOOL)testSemaphore {
__block BOOL resultValue = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"创建信号量");
[self semaphoreBlock:^{
resultValue = YES;
NSLog(@"开始释放信号量,%@", [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
NSLog(@"结束释放信号量,%@", [NSThread currentThread]);
}];
NSLog(@"等待信号量,%@", [NSThread currentThread]);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行returen操作啦");
return resultValue;
}
- (void)semaphoreBlock:(void(^)(void))block {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"进来block了,%@", [NSThread currentThread]);
block();
});
}
一般我们在使用第三方时,例如下载图片或者其他耗时的操作,会在子线程中操作,然后再在主线程回调
,暴露给开发者处理刷新或者其他操作;如果此时在主线程回调中处理释放信号
,那么就会可能出现卡死的情况。所以当我们不能控制接口调用线程时,最好不要在的当前线程永久等待信号量。
所以,一般当我们用第三方中使用信号量出现卡死的情况时,大部分的原因是因为他们的暴露的方法的回调是在主线中执行的~~~