【问题标题】:How to stop a DispatchWorkItem in GCD?如何在 GCD 中停止 DispatchWorkItem?
【发布时间】:2016-11-17 02:59:46
【问题描述】:

我目前正在玩Grand Central Dispatch,发现了一个名为DispatchWorkItem 的类。文档似乎有点不完整,所以我不确定是否以正确的方式使用它。我创建了以下 sn-p 并期望有所不同。我预计该项目将在调用cancel 后被取消。但由于某种原因,迭代仍在继续。任何想法我做错了什么?代码对我来说似乎很好。

@IBAction func testDispatchItems() {
    let queue = DispatchQueue.global(attributes:.qosUserInitiated)
    let item = DispatchWorkItem { [weak self] in
        for i in 0...10000000 {
            print(i)
            self?.heavyWork()
        }
    }

    queue.async(execute: item)
    queue.after(walltime: .now() + 2) {
        item.cancel()
    }
}

【问题讨论】:

    标签: swift grand-central-dispatch cancellation dispatchworkitem


    【解决方案1】:

    没有异步 API 调用“取消”方法将取消正在运行的操作。在每一种情况下,“取消”方法都会做一些事情,以便操作可以查明它是否被取消,并且操作必须不时检查这一点,然后自己停止做更多的工作。

    我不知道有问题的 API,但通常类似于

            for i in 0...10000000 {
                if (self?.cancelled)
                    break;
    
                print(i)
                self?.heavyWork()
            }
    

    【讨论】:

      【解决方案2】:

      GCD 不执行抢先取消。因此,要停止已经开始的工作项,您必须自己测试是否取消。在 Swift 中,cancelDispatchWorkItem。在 Objective-C 中,在您使用 dispatch_block_create 创建的块上调用 dispatch_block_cancel。然后,您可以在 Swift 中使用 isCancelled(在 Objective-C 中称为 dispatch_block_testcancel)来测试是否已取消。

      func testDispatchItems() {
          let queue = DispatchQueue.global()
      
          var item: DispatchWorkItem?
      
          // create work item
      
          item = DispatchWorkItem { [weak self] in
              for i in 0 ... 10_000_000 {
                  if item?.isCancelled ?? true { break }
                  print(i)
                  self?.heavyWork()
              }
              item = nil    // resolve strong reference cycle of the `DispatchWorkItem`
          }
      
          // start it
      
          queue.async(execute: item!)
      
          // after five seconds, stop it if it hasn't already
      
          queue.asyncAfter(deadline: .now() + 5) {
              item?.cancel()
              item = nil
          }
      }
      

      或者,在 Objective-C 中:

      - (void)testDispatchItem {
          dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
      
          static dispatch_block_t block = nil;  // either static or property
      
          __weak typeof(self) weakSelf = self;
      
          block = dispatch_block_create(0, ^{
              for (long i = 0; i < 10000000; i++) {
                  if (dispatch_block_testcancel(block)) { break; }
                  NSLog(@"%ld", i);
                  [weakSelf heavyWork];
              }
      
              block = nil;
          });
      
          // start it
      
          dispatch_async(queue, block);
      
          // after five seconds, stop it if it hasn't already
      
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
              if (block) { dispatch_block_cancel(block); }
          });
      }
      

      【讨论】:

      • DispatchWorkItem 取消后可以重复使用吗?
      • @Rob - 这不会创建一个保留周期吗?
      • @mattsven - 很好。你是绝对正确的。不幸的是,在实例化DispatchWorkItem(解决强引用循环的典型方法)时,您不能使用[weak item][unowned item] 模式,因此您必须在闭包结束时手动nil item就像我在修改后的答案中一样。 (但我们可以在 asyncAfter cancel 的块中使用典型的 [weak item] 模式。)
      • @Rob 这也是我的第一个想法。不幸的是,如果DispatchItem在执行之前被取消,内存就会泄漏。
      • 我在代码注释中添加了“DispatchWorkItem”的限定词...
      猜你喜欢
      • 1970-01-01
      • 2015-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-16
      • 2017-10-17
      • 2020-10-19
      相关资源
      最近更新 更多