微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

DispatchQueue Barrier 未按预期工作

如何解决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 有效引入的),你将自己暴露在各种可能的僵局风险中。

通常,当我们想避免超过工作线程的数量时,而不是分派任意数量的 asyncsync 调用,我们使用 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") } ,我们将远离最大工作线程数。这避免了线程爆炸以及随之而来的问题。


一些注意事项:

  1. 我们还应该指出,当您并发调度到调度队列时,存在竞争条件,您无法确定它们到达调度语句的顺序。例如,上面的示例生成了以下输出:

    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
    ...
    

    这本身不是问题,因为在编写大规模并行化例程时,我们总是这样编写,即执行顺序无关紧要。但是您应该意识到这一点并将其纳入您的设计中。

  2. 确保向每个线程分派足够的代码以证明额外线程的开销是合理的。在这样的例子中,并行版本实际上会比串行版本慢。现在,我假设这只是一个一次性的实验,但请记住“更多线程”并不总是更好。您希望将其设计为平衡每个线程的工作负载。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?