38

我目前正在玩Grand Central Dispatch并发现了一个名为DispatchWorkItem. 文档似乎有点不完整,所以我不确定是否以正确的方式使用它。我创建了以下代码段并期望有所不同。我预计该项目将在调用后被取消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()
    }
}
4

2 回答 2

68

GCD 不执行抢先取消。因此,要停止已经开始的工作项,您必须自己测试是否取消。在 Swift 中cancelDispatchWorkItem. 在 Objective-C 中,调用dispatch_block_cancel你用dispatch_block_create. 然后,您可以在 Swift(在 Objective-C 中isCancelled称为)中测试是否已取消。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); }
    });
}
于 2016-07-14T10:57:34.883 回答
0

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

我不知道有问题的 API,但通常它会像

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

            print(i)
            self?.heavyWork()
        }
于 2016-07-14T09:18:50.373 回答