【问题标题】:DispatchQueue Barrier Not Working As ExpectedDispatchQueue 屏障未按预期工作
【发布时间】:2021-06-01 13:50:35
【问题描述】:

我在操场上有这段代码

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 之间的唯一区别是它是否会阻塞其调用者线程。

有人可以解释这种行为吗?

【问题讨论】:

  • 竞态条件,堆积线程?

标签: swift asynchronous synchronous barrier dispatch-queue


【解决方案1】:

关键问题是全局队列上的线程爆炸。如果您的迭代次数低于 64(每个 QoS 的最大工作线程数),那么您的代码应该可以正常工作。但是当你超过这个值时(尤其是当你在那里有一个“等待”时,这是sync 有效地引入的),你就会将自己暴露在各种可能的死锁风险中。

通常,当我们想要避免超过工作线程的数量,而不是调度任意数量的asyncsync 调用时,我们使用concurrentPerform,这是一个并行化的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")
}

通过使用concurrentPerform,我们将远离工作线程的最大数量。这避免了线程爆炸以及随之而来的问题。


一些注意事项:

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

    异步 0 同步 0 异步 1 同步 1 异步 3 同步 3 异步 2 同步 2 异步 4 同步 4 异步 6 同步 6 异步 5 同步 5 ...

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

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

【讨论】:

    猜你喜欢
    • 2023-01-28
    • 2019-09-04
    • 2021-05-15
    • 2021-06-04
    • 2022-01-24
    • 2015-05-11
    • 2020-05-15
    • 2014-10-31
    • 2018-02-12
    相关资源
    最近更新 更多