iOS/Swift中DispatchQueue并发测试不符预期,主队列执行顺序存疑
首先来解答你第一个核心疑问:为什么主队列的Main输出会排在最后?
你可能混淆了队列优先级和主队列的执行机制。主队列确实和主线程绑定,且本身属于高优先级队列,但它有个关键特性:主队列的异步任务不会抢占当前正在主线程运行的代码。
在你的第一段代码里,当调用DispatchQueue.main.async时,这个任务只是被添加到了主队列的末尾,但此时主线程正忙着执行Playground里的同步代码(比如导入框架、设置needsIndefiniteExecution、以及其他几个async调用的逻辑)。只有当主线程当前所有同步执行的代码都完成后,Runloop才会去处理主队列里等待的任务——哪怕主队列QoS优先级很高,也得等主线程当前的工作做完,所以Main的五次打印才会排在最后。
你理解的User Interactive和Background输出顺序随机是对的:不同QoS的全局队列优先级有差异,但系统调度线程时会根据当前负载等情况动态调整,高优先级任务只是有更高的执行概率,并非每次都严格按优先级顺序完成。
再看你补充的代码:
print("BEGINNING OF CODE") DispatchQueue.main.async { for _ in 1...5 { print("Main") } } DispatchQueue.global().sync { for _ in 1...5 { print("Sync Global") } } DispatchQueue.global(qos: .background).async { for _ in 1...5 { print("Background") } } DispatchQueue.global(qos: .userInteractive).async { for _ in 1...5 { print("User Interactive") } } print("END OF CODE")
对应的输出:
BEGINNING OF CODE
Sync Global
Sync Global
Sync Global
Sync Global
Sync Global
END OF CODE
User Interactive
Background
User Interactive
User Interactive
User Interactive
User ...
这里的关键点是sync调用的行为:sync会阻塞当前调用线程(也就是主线程),直到这个同步任务执行完成。所以当你调用DispatchQueue.global().sync时,主线程会立刻暂停当前代码执行,转而等待这个全局队列的任务完成——也就是先打印五次Sync Global,之后才会继续执行后面的async调用和print("END OF CODE")。
同样,主队列的Main任务还是要等主线程当前所有同步代码(从BEGINNING到END OF CODE的所有逻辑)执行完毕,才会被Runloop处理,所以它的打印会在所有异步任务之后(如果你的输出没写完,最后应该能看到Main的五次打印)。
最后总结几个核心知识点:
- 主队列的异步任务必须等待主线程当前Runloop周期内的任务全部完成,才会开始执行
sync调用会阻塞当前线程,必须等任务完成才会继续后续代码- QoS优先级影响的是系统调度线程的概率,但主队列的执行时机和QoS无关,只和主线程的空闲状态有关
内容的提问来源于stack exchange,提问作者Michael Hsu

