首页 > 解决方案 > Swift:如何中断排序例程

问题描述

如果需要太长时间,我想让用户能够停止排序程序。

我尝试使用 DispatchWorkItem.cancel。但是,这实际上并没有停止已经启动的进程。

let myArray = [String]() // potentially 230M elements!
...
workItem = DispatchWorkItem {
    let result = myArray.sorted()
    DispatchQueue.main.async {
    print ("done")
    }
}

// 如果进程太长,用户点击“取消” => workItem.cancel() // 不会停止排序

如何杀死 workItem 的线程?

我无权访问已排序的例程,因此我无法插入测试以检查当前线程是否处于“已取消”状态...

标签: multithreadingsortinggrand-central-dispatchdispatchworkitem

解决方案


isCancelled正如您所推断的,如果不定期检查并手动执行提前退出(如果已设置),则根本无法杀死工作项。

有两种选择:

  1. 您可以在那里使用sorted(by:), 测试,isCancelled如果它被取消则抛出错误。这实现了预期的提前退出。

    这可能看起来像:

    func cancellableSort() -> DispatchWorkItem {
        var item: DispatchWorkItem!
        item = DispatchWorkItem() {
            let unsortedArray = (0..<10_000_000).shuffled()
            let sortedArray = try? unsortedArray.sorted { (obj1, obj2) -> Bool in
                if item.isCancelled {
                    throw SortError.cancelled
                }
                return obj1 < obj2
            }
            // do something with your sorted array
            item = nil
        }
        DispatchQueue.global().async(execute: item)
        return item
    }
    

    在哪里

    enum SortError: Error {
        case cancelled
    }
    

    请注意,即使在发布版本中,这也会对性能产生巨大影响。所以你可能想对此进行基准测试。

  2. 您可以编写自己的排序例程,isCancelled在算法中插入您自己的测试。这使您可以更好地控制执行测试的精确位置(即,您可能不想在每次比较时都这样做,而是在算法中的某个更高级别的循环中进行,从而最大限度地减少对性能的影响)。鉴于记录的数量,这使您有机会选择最适合您的数据集的算法。

显然,在对这些替代方案进行基准测试时,请确保您测试优化/发布版本,以便您的结果不会因构建设置而出现偏差。

顺便说一句,您也可以考虑使用Operation,因为它对取消的处理更加优雅,恕我直言。另外,您可以有一个专门的对象用于排序操作,这样更干净。


推荐阅读