Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to implement dispatch_cancel()?

So far I have gone through the doc of GCD, however it seems there misses dispatch_cancel() which I want to use it to cancel all dispatch's blocks invocation. Is there any way to implement dispatch_cancel()?

like image 449
Richard Avatar asked Sep 12 '13 07:09

Richard


2 Answers

As @HampusNilsson mentions, you can't reasonably cancel any in-flight operation in a non-garbage collected environment (such as this) because it would inherently leak resources and leave the process in an indeterminate state. NSOperationQueue has a cancellation API, and that API can be used to implement cancellation of in-flight operations, provided that the operations themselves are cooperatively checking the flag and then cleaning up and returning early. It's not a true, hard abort.

As for canceling enqueued-but-not-started work items, yes, NSOperationQueue handles this, but that comes at some additional expense, and NSOperationQueue is a higher level of abstraction. GCD's performance is largely predicated on the internal use of lock-free queues. A lock-free queue is going to be faster than a lock-based implementation, but it's going to require certain trade-offs in order to achieve that speed. For instance, I would expect it to be much harder to arbitrarily mutate the queue in a lock free manner to remove a cancelled operation. I suspect that limiting the exposed queue operations to "enqueue only," and making the work items themselves immutable (blocks and function ptrs), opened the door for many of the optimizations that allow GCD to have such little overhead and perform so well.

FWIW, in the common case, making operations cancel-able is pretty trivial to implement on top of the existing GCD API, so anyone who needs this functionality can pretty easily do it themselves (and likely in a way that's better suited to their specific needs than a generalized API would be). Consider the following function -- it enqueues a block on a queue and returns a block that you could call later to cancel the enqueued operation:

dispatch_block_t dispatch_cancelable_async(dispatch_queue_t q, dispatch_block_t b)
{
    __block uintptr_t isCancelled = 0;
    dispatch_async(q, ^{
        if (!isCancelled) b();
    });
    return [[^{ isCancelled = 1; } copy] autorelease];
}

This won't be the right cancellation method for every case, but it's a decent first approximation.

"Use the highest-level abstraction that gets the job done." If you want cancellation, and the difference in overhead between NSOperationQueue and GCD is not a significant factor, you should just use NSOperationQueue. Some would even go as far as to argue that using NSOperationQueue is the more idiomatic choice when working in Objective-C. Beyond that, implementing non-aborting cancellation for the common case on top of GCD is, as shown, reasonably trivial.

Based on all that, my suspicion is that building in cancellation to the API was not a worthwhile trade-off in terms of performance and complexity.

like image 108
ipmcc Avatar answered Oct 08 '22 23:10

ipmcc


from iOS 8 and higher you can cancel block execution with
dispatch_block_cancel

dispatch_block_t blockTask = dispatch_block_create(0,{
 //do some task
});
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,5*NSEC_PER_SEC);
dispatch_after(time,dispatch_get_main_queue(),blockTask);

dispatch_block_cancel(blockTask);
like image 44
Marat Ibragimov Avatar answered Oct 08 '22 22:10

Marat Ibragimov