保活线程中调用信号量会发生什么?
看看下面这个例子。
@property (nonatomic, strong)NSThread *thread;
@property (nonatomic, strong)dispatch_semaphore_t semap;
self.thread = [[NSThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}];
[self.thread start];
self.semap = dispatch_semaphore_create(0);
[self performSelector:@selector(threadTest) onThread:self.thread withObject:nil waitUntilDone:YES];
- (void)threadTest{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"11");
dispatch_semaphore_signal(self.semap);
});
dispatch_semaphore_wait(self.semap, DISPATCH_TIME_FOREVER);
NSLog(@"%@",[NSThread currentThread]);
}
上面这段代码的执行结果是什么呢?
用心想一下。
实际运行结果,是会卡在 dispatch_semaphore_wait(self.semap, DISPATCH_TIME_FOREVER);这个上面。
不会走 dispatch_async(dispatch_get_main_queue() 的方法。走最后一行代码。
但是如果不包活线程,直接调用,那么能正常打印。
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil];
[self.thread start];
这样搞,就真的很奇怪了。
于是尝试了把dispatch_async(dispatch_get_main_queue(),改为dispatch_async(dispatch_get_global_queue(0, 0),发现先打印了 NSLog(@"11");于是我就在考虑是不是发生了死锁。
然后看到performSelector这个方法,在前面设计线程保活的时候,用到过waitUntilDone这个参数。
waitUntilDone 一个布尔值,指定当前线程是否阻塞,直到在指定线程上的接收器上执行指定选择器之后。 指定 YES 来阻塞这个线程; 否则,指定 NO 以立即返回此方法。
当我把waitUntilDone:NO,发现问题就解决了。
[self performSelector:@selector(threadTest) onThread:self.thread withObject:nil waitUntilDone:NO];
原因是什么呢?
waitUntilDone == YES 导致主队列的方法必须等待threadTest执行完,才能继续往下执行,而threadTest执行完,又要等待加入dispatch_async(dispatch_get_main_queue(), ^{}中的方法执行完,才能执行。这样就形成了死锁。