Is there any way to implement dispatch_cancel()?

半世苍凉 提交于 2019-11-29 07:54:20

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.

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);

GCD does not implement a cancellation API, as it would not be safe (it would potentially abort threads mid-operation). If you want to cancel tasks you need to do it yourself through implementing a "cancelled" boolean and checking it in your tasks when they start and later on periodically.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!