分割递归任务

时间:2018-04-19 13:47:58

标签: swift recursion concurrency grand-central-dispatch

我正在处理几何类型,可以细分为自己的实例。此功能由以下协议表示:

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没有正确等待,而是全局队列丢弃项目。将queueDispatchQueue.global()更改为DispatchQueue(label: "subdivision", attributes: .concurrent)可以产生正确的行为。

我还最终将整个函数体包装在withoutActuallyEscaping(block) { block in ... }中,这样输入闭包不需要@escaping,这在使用全局队列时不会导致崩溃,如果在group.wait()之后实际执行了任务,那将会有。对我来说,无论出于何种原因,这都是以静默方式丢弃项目的全局队列,对吗?

这怎么可能,为什么自定义并发队列不会显示相同的行为?

编辑第2号

根据this answer障碍,全局队列不支持。他们似乎很乐意接受它们,但随后忽略了标志,同时运行任务,导致未定义的行为。

我无法在任何地方找到正确记录的内容,是否有人有指示?

0 个答案:

没有答案