如何解决DispatchQueue Barrier 未按预期工作
我在操场上有这个代码
import PlaygroundSupport
import Foundation
PlaygroundPage.current.needsIndefiniteExecution = true
let queue = dispatchQueue(label: "Barrier Test",attributes: .concurrent)
func syncProcess(index: Int) {
queue.sync {
print("Sync \(index) Called!")
}
}
func asyncBarrier(index: Int) {
queue.async(flags: .barrier) {
print("Async Barrier \(index) Called!")
}
}
for i in 0...1000 {
dispatchQueue.global().async {
asyncBarrier(index: i)
}
dispatchQueue.global().async {
syncProcess(index: i)
}
}
这是输出:
Async Barrier 0 Called!
Sync 0 Called!
但是当我尝试将迭代次数从 1000 次减少到更小(即 10 次)时,它可以正常工作
Async Barrier 0 Called!
Sync 0 Called!
Async Barrier 1 Called!
Sync 1 Called!
Async Barrier 2 Called!
Sync 2 Called!
Async Barrier 3 Called!
Sync 3 Called!
Async Barrier 4 Called!
Sync 4 Called!
Async Barrier 5 Called!
Sync 5 Called!
Async Barrier 6 Called!
Sync 6 Called!
Async Barrier 7 Called!
Sync 7 Called!
Async Barrier 8 Called!
Sync 8 Called!
Sync 9 Called!
Async Barrier 9 Called!
Async Barrier 10 Called!
Sync 10 Called!
我只是想知道,发生了什么?
如果我将 asyncBarrier 更改为 syncBarrier,即使我使用了 1000 次迭代,它也能正常工作。
AFAIK,asyncBarrier 和syncBarrier 之间的唯一区别是它是否会阻塞其调用者线程。
有人可以解释这种行为吗?
解决方法
关键问题是全局队列上的线程爆炸。如果您的迭代次数保持在 64(每个 QoS 的最大工作线程数)以下,那么您的代码应该可以正常工作。但是当你超过这个范围时(特别是当你在那里“等待”时,这就是 sync
有效引入的),你将自己暴露在各种可能的僵局风险中。
通常,当我们想避免超过工作线程的数量时,而不是分派任意数量的 async
或 sync
调用,我们使用 concurrentPerform
,这是一个并行化的 { {1}} 循环,受限于设备上的内核数:
for
通过使用 let queue = DispatchQueue(label: "Barrier Test",attributes: .concurrent)
DispatchQueue.global().async {
DispatchQueue.concurrentPerform(iterations: 1_000) { index in
queue.async(flags: .barrier) {
print("async",index)
}
queue.sync {
print("sync",index) }
}
}
print("all done")
}
,我们将远离最大工作线程数。这避免了线程爆炸以及随之而来的问题。
一些注意事项:
-
我们还应该指出,当您并发调度到调度队列时,存在竞争条件,您无法确定它们到达调度语句的顺序。例如,上面的示例生成了以下输出:
async 0 sync 0 async 1 sync 1 async 3 sync 3 async 2 sync 2 async 4 sync 4 async 6 sync 6 async 5 sync 5 ...
这本身不是问题,因为在编写大规模并行化例程时,我们总是这样编写,即执行顺序无关紧要。但是您应该意识到这一点并将其纳入您的设计中。
-
确保向每个线程分派足够的代码以证明额外线程的开销是合理的。在这样的例子中,并行版本实际上会比串行版本慢。现在,我假设这只是一个一次性的实验,但请记住“更多线程”并不总是更好。您希望将其设计为平衡每个线程的工作负载。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。