同步等待AVAssetExportSession.exportAsynchronously完成是否会触发死锁?
问题结论
这种写法存在明确的死锁风险,AVAssetExportSession.exportAsynchronously 并没有官方保证回调一定会运行在其他队列上。
具体原因
- 苹果官方文档对该方法的回调队列没有做强制约定:如果你没有主动设置
AVAssetExportSession的completionQueue属性,回调默认会派发到任意全局后台队列执行;但如果你自行指定了completionQueue,回调就一定会跑到你指定的队列上执行。 - 死锁的触发场景非常容易复现:如果调用
foo()的当前队列,刚好和回调执行的队列是同一个串行队列,就会出现死锁:当前线程被semaphore.wait()阻塞挂起,回调任务在同一个串行队列里等待执行,永远等不到semaphore.signal()触发。 - 额外风险:如果是在主队列调用这个
foo()方法,哪怕没有触发死锁,阻塞主线程也会直接导致UI完全无响应,违反苹果的主线程优化规则。
优化方案
如果确实有同步等待导出完成的需求(比如当前已经在后台串行队列执行任务,需要保证执行顺序),可以主动指定独立的回调队列,避免和当前调用队列重合:
// 注意:该方法绝对不可以在主队列调用 func foo() { let semaphore = DispatchSemaphore(value: 0) // 主动指定独立的并发队列作为回调队列,避免和当前队列冲突 session.completionQueue = DispatchQueue(label: "custom.export.completion", attributes: .concurrent) session.exportAsynchronously { defer { semaphore.signal() } // 处理导出结果逻辑 } semaphore.wait() }
最佳实践:更推荐全程用异步逻辑处理导出流程,不要强行把异步方法转同步,能彻底规避死锁、线程阻塞的问题。
内容的提问来源于stack exchange,提问作者whatdayistoday
相关产品推荐
相关产品推荐

