我正在处理几何类型,可以细分为自己的实例。此功能由以下协议表示:
protocol Subdividable {
func subdivision(using block: (Self) -> ())
}
Subdividable
的实现可能如下所示:
struct Tile { ... }
extension Tile: Subdividable {
func subdivision(using block: (Tile) -> ()) {
if condition() {
let (a, b) = createSubTiles()
block(a)
block(b)
} else {
let (a, b, c) = createDifferentSubTiles()
block(a)
block(b)
block(c)
}
}
}
给定类型细分的实例数量不固定,可能依赖于细分实例的属性,也可能不依赖于细分实例的属性。创建后,每个新实例都会传递给block
。
要创建最终结果,我需要递归地应用这些细分次数:
extension Subdividable {
func subdivision(level: Int, using block: (Self) -> ()) {
switch level {
case 0:
return
case 1:
subdivision(using: block)
default:
precondition(level > 1)
subdivision { value in
value.subdivision(level: level - 1, using: block)
}
}
}
}
由于通常无法从一开始就预测,block
将被调用多少次,因此还需要跟踪索引。 (即将结果存储在缓冲区中)
extension Subdividable {
@discardableResult func subdivision(level: Int, using block: (Int, Self) -> ()) -> Int {
var result = 0
subdivision(level: level) { value in
block(result, value)
result += 1
}
return result
}
}
到目前为止,这么好。由此产生的几何结构可能非常复杂,可能包含数百万个元素,这使得性能成为一个问题。这就是为什么我试图将递归细分分成多个并发任务的原因,然而它确实不能可靠地累积每个任务的结果:
extension Subdividable {
@discardableResult func subdivision(level: (lhs: Int, rhs: Int), using block: @escaping (Self) -> (Int, Self) -> ()) -> Int {
var result = 0
let (queue, group) = (DispatchQueue.global(), DispatchGroup())
subdivision(level: level.lhs) { value in
queue.async(group: group) {
let count = value.subdivision(level: level.rhs, using: block(value))
// not all barrier tasks will have finished after group.wait()
queue.async(group: group, flags: .barrier) {
result += count // accumulate result
}
}
}
group.wait()
return result
}
}
为什么等待group
不能保证内部async
来电完成?
我还尝试使用自定义串行队列实现上述功能,以将累积块推送到,并且可以正常工作,始终给出正确的结果。我只是不明白为什么这不等同于将它作为屏障任务推送到并发队列。
我在这里缺少什么?有没有办法实现这个而不需要自定义队列?
编辑第1号
问题似乎不是group
没有正确等待,而是全局队列丢弃项目。将queue
从DispatchQueue.global()
更改为DispatchQueue(label: "subdivision", attributes: .concurrent)
可以产生正确的行为。
我还最终将整个函数体包装在withoutActuallyEscaping(block) { block in ... }
中,这样输入闭包不需要@escaping
,这在使用全局队列时不会导致崩溃,如果在group.wait()
之后实际执行了任务,那将会有。对我来说,无论出于何种原因,这都是以静默方式丢弃项目的全局队列,对吗?
这怎么可能,为什么自定义并发队列不会显示相同的行为?
编辑第2号
根据this answer障碍,全局队列不支持。他们似乎很乐意接受它们,但随后忽略了标志,同时运行任务,导致未定义的行为。
我无法在任何地方找到正确记录的内容,是否有人有指示?