【问题标题】:Swift: How to interrupt a sort routineSwift:如何中断排序例程
【发布时间】:2019-09-18 03:12:09
【问题描述】:

我想让用户能够在排序程序耗时过长时停止它。

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

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

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

如何杀死 workItem 的线程?

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

【问题讨论】:

    标签: multithreading sorting grand-central-dispatch dispatchworkitem


    【解决方案1】:

    正如您所推断的,如果不定期检查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,因为它对取消的处理更加优雅,恕我直言。另外你可以有一个专门的对象来进行排序操作,这样更干净。

    【讨论】:

    • 谢谢罗布;我将使用 sorted(by:) 并在每个比较中嵌入一个 isCancelled 测试。我不清楚操作取消处理如何更优雅:我仍然需要在每次比较中添加一个 Operation.isCancelled 测试,不是吗?更一般地说,我很惊讶没有“kill pid”方法来简单地杀死一个线程......
    • Re Operation 优雅,这是一件小事,但我讨厌让 DispatchWorkItem 可取消的愚蠢(首先将项目声明为 var,然后实例化,然后检查 @987654332 @,最后,确保将item 设置为nil,否则它不会被释放)。使用Operation 子类,(a) 本着 SRP 的精神,一切都被包裹在那个单独的对象中;并且 (b) 您不必担心在 DispatchWorkItem 闭包中检查 isCancelled 导致的强引用循环。这是次要的,但我只是发现 DispatchWorkItem 接近 kludgy。
    • Re no “kill pid” 等同于线程,我同意这会很好,但问题是除非你解除所有内存管理调用,否则它会泄漏内存。如果您的例程抛出错误或在例程中采用其他“提前退出”方法,它会执行所有必要的内存展开。但是当一个人杀死整个 pid 时,它只会丢弃与该进程相关的所有内存,但这种方法不适用于多线程应用程序中的单个线程。
    • 有道理。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2016-10-17
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多