我目前正在尝试解决0x8BADF00D
。
faultingThread
是 0,我想这是主线程。但是,我不认为我正在做的大量工作实际上在主线程中下降。
在我做的在主线程上运行的函数中
DispatchQueue.global(qos: .userInitiated).async {
// ... heavy work
// and after the work is done I do
DispatchQueue.main.asyncAfter(deadline: .now()+1.0) {
// ... displaying heavy work
}
}
我的逻辑有明显的错误吗?我以为 .userInitiated 会离开主线程,尤其是在.async
上。
"exception" : {"codes":"0x0000000000000000,0x0000000000000000","rawCodes":[0,0],"type":"EXC_CRASH","signal":"SIGKILL"}, "终止" : {"flags":6,"code":2343432205,"namespace":"FRONTBOARD","reason":["<RBSTerminateContext|>:579 耗尽实际(挂钟)时间允许 10.00 秒","进程可见性: 背景","进程状态: 正在运行","监视器事件: 场景更新","看门狗可见性:背景","看门狗CPU静态:(","经过的总CPU时间(秒):21.740(用户21.740,系统0.000),99%CPU",","经过的应用程序CPU时间(秒):9.832,45%CPU",") 报告类型:崩溃日志最大终止电阻:交互式>"]}, "错误线程" : 0,
这里的模式是正确的(尽管建议使用更合理的QoS)。您的看门狗问题(主线程被阻塞)位于其他地方。但是,将昂贵的进程调度到后台队列,然后将 UI/模型更新调度回主队列是正确的过程。
注意:在某些情况下,此常规模式可能会阻塞主线程。具体来说,如果你有线程爆炸(例如,超过64个任务调度到该全局队列),则可以阻塞/死锁。GCD 的线程池非常有限(此时为 64 个),如果超过此值,后续调度到该队列的尝试将阻塞,直到队列被释放。这通常只是在使用锁、信号量或以其他方式等待时的问题。不幸的是,您的代码片段中没有足够的内容供我们诊断这种特殊情况下的问题。
因此,如果出现线程爆炸,则应重构以限制最大并发程度。操作队列maxConcurrentOperation
来促进这一点。GCD 的concurrentPerform
不会超过在任何给定时间允许运行的最大线程数。Swift 5.5 协作线程池也将并行性限制在合理的限制内。Combine 具有"最大发布者"来控制并发程度。过去,我们可能使用非零信号量来限制并发程度(尽管现在我们倾向于使用上述当代解决方案之一)。有很多方法可以缓解线程爆炸。
所有这些都假设问题在于线程爆炸(如果嵌套的async
调度阻塞,这可能是罪魁祸首)。如果你没有线程爆炸,那么你只是有其他东西,完全不相关,阻塞了主线程。
QoS 确定系统仅计划执行任务的优先级。
您可以随时使用Thread.current
检查当前线程:
DispatchQueue.global(qos: .userInitiated).async {
print(Thread.current.description)
}
输出:
<NSThread: 0x6000005bde00>{number = 7, name = (null)}`
主线程只有number = 0
和name = main
,因此您的代码在后台线程上运行,您可以对其进行繁重的工作。