web-dev-qa-db-fra.com

Comment arrêter un DispatchWorkItem dans GCD?

Je joue actuellement avec Grand Central Dispatch et ai découvert une classe appelée DispatchWorkItem. La documentation semble un peu incomplète, je ne suis donc pas sûre de l'utiliser correctement. J'ai créé l'extrait suivant et j'attendais quelque chose de différent. Je m'attendais à ce que l'article soit annulé après avoir appelé cancel dessus. Mais l'itération continue pour une raison quelconque. Des idées que je fais mal? Le code semble bien pour moi. 

@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()
    }
}
22
Sebastian Boldt

GCD n'effectue pas d'annulations préemptives. Par conséquent, pour arrêter un élément de travail qui a déjà commencé, vous devez tester vous-même les annulations. Dans Swift, cancel the DispatchWorkItem . En Objective-C, appelez dispatch_block_cancel sur le bloc que vous avez créé avec dispatch_block_create . Vous pouvez ensuite vérifier si/a été annulé ou non avec isCancelled dans Swift (connu sous le nom de dispatch_block_testcancel dans 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()
    }
}

Ou, en 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); }
    });
}
43
Rob

Il n'y a pas d'API asynchrone où appeler une méthode "Cancel" annulera une opération en cours. Dans tous les cas, une méthode "Annuler" fera quelque chose pour que l'opération puisse savoir si elle est annulée, et l'opération doit vérifier cela de temps en temps, puis cesser de travailler davantage par elle-même. 

Je ne connais pas l'API en question, mais ce serait généralement quelque chose comme: 

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

            print(i)
            self?.heavyWork()
        }
0
gnasher729