轮询GCD主队列,以避免死锁。

问题描述 投票:0回答:1

我有一个iOS应用,有多个线程,在一个后台线程中,我运行一些第三方代码。在一个后台线程中,我运行一些第三方代码.第三方代码偶尔会调用。

dispatch_sync(dispatch_get_main_queue(), block);

回调必须是_sync,因为它需要答案,而且它需要在主线程上,因为它调用的是 UIApplication.

当我需要关闭后台线程时,问题就会出现。关机源自UI,而且还必须同步。所以,我有时会看到一个死锁。

我试着用下面描述的方法来解决这个问题。此处. 基本上是在循环中调用NSRunLoop,直到后台线程设置了一个标志.像这样。

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];

然而,这并不奏效 我在调试器中可以看到后台线程挂在dispatch_sync调用上,即使NSRunLoop runUntilDate被多次调用。

下面是后台线程的调用栈,它被挂起了。

enter image description here

这里是主线程的调用栈 (它没有崩溃,只是在调试器中暂停了):

enter image description here

我想这一定是运行模式的问题,但我不知道是什么,也不知道如何解决。 那么我如何能在主线程忙于循环的时候,为GCD主队列提供服务呢?

ios multithreading grand-central-dispatch deadlock nsrunloop
1个回答
0
投票

那个 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];
    });
});
© www.soinside.com 2019 - 2024. All rights reserved.