Reputation: 699
I read the article: GCD global concurrent queue not always concurrent(iOS device)? and this is what I want to clarify:
Documentation ( https://developer.apple.com/documentation/dispatch/1431058-dispatch_block_cancel ) says that tasks that started execution cannot be canceled.
is it correct to suggest that any block dispatched into global concurrent queue will automatically start the execution and thus any attempt of cancellation will effectively fail?
To be more specific - if I enqueue 1000 tasks, while each one takes 1000 seconds to accomplish, will it mean that in time less than 1000 secs all tasks will start the execution and cancellation of any of them will fail?
Is the answer the same for iOS and OSX, or not?
UPD: I want to make this question even clearer. Is it possible that for a given number of tasks significantly excessing computational capabilities of the device there will be a situation when we will fail to cancel tasks queued in the global concurrent queue because all of them where started?
Upvotes: 0
Views: 313
Reputation: 437452
is it correct to suggest that any block dispatched into the global concurrent queue will automatically start the execution and thus any attempt of cancellation will effectively fail?
Not quite. First, a little hair-splitting: Just because you’ve dispatched something to a global queue does not guarantee that it has started yet. The global queues have a limited number of worker threads, so the timing of the starting of the dispatched task is subject to resource availability. Sure, if the queue is free, then it will generally start it fairly quickly, but in high contention situations, some of the latter tasks might not start immediately.
Second, that documentation is correct that if one cancels a task that has already started, it will not automatically stop it for you. But we can achieve this behavior if we (and should) write tasks that periodically check dispatch_block_testcancel
(isCancelled
in Swift), and if so, terminate. For example:
- (void)testDispatchItem {
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
__block dispatch_block_t block = nil;
__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); }
});
}
... if I enqueue 1000 tasks ...
Never dispatch that many tasks to a global queue because you will exhaust the very limited (64 IIRC, though that’s subject to change) worker threads. That can cause a variety of issues, and in some cases, deadlocks. See “Thread Explosion Causing Deadlock” discussion in WWDC 2015 video Building Responsive and Efficient Apps with GCD. Also, see WWDC 2016 Concurrent Programming With GCD in Swift 3.
Is it possible that for a given number of tasks significantly [exceeding] computational capabilities of the device there will be a situation when we will fail to cancel tasks queued in the global concurrent queue because all of them [were] started?
Obviously, tasks that haven’t started yet just will be canceled. And regarding those tasks that have started, as long as you properly test for cancellation state, then it’s a non-issue.
Upvotes: 3
Reputation: 301
once a dispatch task has started running, neither canceling or suspending the task/queue/work item will stop it. Canceling and suspending operations only affect tasks that haven’t yet been started.
In your example, all task is enqueued, but which task will start first is not guaranteed though we can provide QOS to every task which is a separate topic.
Suppose we enqueue 1000 tasks, as we know every task start in threads, so it's not possible that all task enqueued will start together. If we assume like we have 10 threads to perform tasks, so out of 1000 enqueued item 10 will start execution and remaining 990 tasks are enqueued. If we cancel at this point then 990 tasks enqueued will get cancel but 10 tasks which have started will complete execution of their respective task.
Upvotes: 0