【问题标题】:Polling GCD main queue, to avoid deadlock轮询 GCD 主队列,避免死锁
【发布时间】:2020-05-13 13:37:35
【问题描述】:

我有一个带有多个线程的 iOS 应用。在后台线程中,我运行了一些第 3 方代码。 3rd 方代码偶尔会调用:

dispatch_sync(dispatch_get_main_queue(), block);

回调必须是 _sync,因为它需要应答并且需要在主线程上,因为它调用了UIApplication。

当我需要关闭后台线程时出现问题。关闭源于 UI,它也必须是同步的。所以,我有时会看到死锁。

我已经尝试通过here 描述的方法解决它。基本上在循环中调用 NSRunLoop,直到后台线程设置了一个标志。 像这样:

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

但是,它不起作用。我可以在调试器中看到后台线程挂在 dispatch_sync 调用上,即使 NSRunLoop runUntilDate 被多次调用。

这里是后台线程的调用栈,被阻塞了:

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

我认为这一定与运行模式有关,但我不确定是什么以及如何解决这个问题。 那么,当主线程忙于循环时,如何为 GCD 主队列提供服务呢?

【问题讨论】:

  • 只是为了澄清:“关闭源自 UI,它也必须是异步的”可能应该是“......它也必须是 sync”?
  • 确实是@Tommy。谢谢你抓住那个。我已经编辑了问题。

标签: ios multithreading grand-central-dispatch deadlock nsrunloop


【解决方案1】:

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 同时阻塞主线程的技术本质上是一种混搭(而且有点不合时宜)。例如。用 GCD 调用替换 finishInTenSeconds 中的计时器,这个闭包永远不会被调用:

- (void)finishInTenSeconds {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.isFinished = true;
    });
}

问题是带有run调用的while循环将允许运行循环处理事件,但它不会改变主线程被原始dispatch_sync调用阻塞的事实。

请注意,您的具体情况无疑是不同的,但希望这能说明NSRunLoop kludge 引入的一般问题。


适当的解决方案是尽量减少同步调用。例如。如果您需要在后台队列上的任务完成后执行某些操作,而不是同步调用,如下所示:

dispatch_sync(self.queue, ^{
    [self foo];
});

[self bar];

您将改为异步分派,然后在完成后将后续代码异步分派回主队列:

dispatch_async(self.queue, ^{
    [self foo];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self bar];
    });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 2012-10-30
    • 1970-01-01
    • 2021-08-02
    相关资源
    最近更新 更多