Подтвердить что ты не робот

Как остановить DispatchWorkItem в GCD?

В настоящее время я играю с 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()
    }
}
4b9b3361

Ответ 1

GCD не выполняет упреждающие отмены. Таким образом, чтобы остановить рабочий элемент, который уже запущен, вы должны самостоятельно проверить отмены. В Swift cancel DispatchWorkItem. В Objective-C вызовите dispatch_block_cancel для блока, который вы создали с помощью dispatch_block_create. Затем вы можете проверить, было ли отменено или нет с помощью isCancelled в Swift (известный как dispatch_block_testcancel в Objective-C).

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 { break }
            print(i)
            self?.heavyWork()
        }
        item = nil    // resolve strong reference cycle
    }

    // start it

    queue.async(execute: item)

    // after five seconds, stop it if it hasn't already

    queue.asyncAfter(deadline: .now() + 5) { [weak item] in
        item?.cancel()
    }
}

Или в 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); }
    });
}

Ответ 2

Нет асинхронного API, где вызов метода "Отмена" отменяет текущую операцию. В каждом отдельном случае метод "Отмена" будет делать что-то, чтобы операция могла выяснить, отменена ли она, и операция должна время от времени проверять это, а затем перестать делать больше работы сама по себе.

Я не знаю API, о котором идет речь, но обычно это было бы что-то вроде

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

            print(i)
            self?.heavyWork()
        }