我有一个iOS应用,有多个线程,在一个后台线程中,我运行一些第三方代码。在一个后台线程中,我运行一些第三方代码.第三方代码偶尔会调用。
dispatch_sync(dispatch_get_main_queue(), block);
回调必须是_sync,因为它需要答案,而且它需要在主线程上,因为它调用的是 UIApplication
.
当我需要关闭后台线程时,问题就会出现。关机源自UI,而且还必须同步。所以,我有时会看到一个死锁。
我试着用下面描述的方法来解决这个问题。此处. 基本上是在循环中调用NSRunLoop,直到后台线程设置了一个标志.像这样。
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
然而,这并不奏效 我在调试器中可以看到后台线程挂在dispatch_sync调用上,即使NSRunLoop runUntilDate被多次调用。
下面是后台线程的调用栈,它被挂起了。
这里是主线程的调用栈 (它没有崩溃,只是在调试器中暂停了):
我想这一定是运行模式的问题,但我不知道是什么,也不知道如何解决。 那么我如何能在主线程忙于循环的时候,为GCD主队列提供服务呢?
那个 NSRunLoop
在某些情况下,这种技术可以发挥作用。
- (void)start {
dispatch_sync(self.queue, ^{
[self finishInTenSeconds];
while (!self.isFinished) {
NSLog(@"loop");
[[NSRunLoop mainRunLoop] runUntilDate:[[NSDate date] dateByAddingTimeInterval:1]];
}
});
}
- (void)finishInTenSeconds {
[NSTimer scheduledTimerWithTimeInterval:10 repeats:false block:^(NSTimer * _Nonnull timer) {
self.isFinished = true;
}];
}
但这种手动调用的技术 run
的主运行循环,同时阻塞主线程,本质上是一种笨拙的做法(而且有点不合时宜)。例如,将 finishInTenSeconds
与GCD调用,而这个闭包永远不会被调用。
- (void)finishInTenSeconds {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.isFinished = true;
});
}
问题是... while
循环与 run
调用将允许运行循环处理事件,但这并不能改变主线程被原来的 dispatch_sync
调用。
请注意,您的情况无疑是不同的,但希望这能说明用 NSRunLoop
笨拙。
适当的解决方案是尽量减少同步调用。比如,如果你需要在后台队列上的任务完成后再做某件事,而不是像这样同步调用。
dispatch_sync(self.queue, ^{
[self foo];
});
[self bar];
你应该异步调度,然后,当它完成后,异步调度后续代码回到主队列。
dispatch_async(self.queue, ^{
[self foo];
dispatch_async(dispatch_get_main_queue(), ^{
[self bar];
});
});