Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop a DispatchWorkItem in GCD?

I am currently playing around with Grand Central Dispatch and discovered a class called DispatchWorkItem. The documentation seems a little incomplete so I am not sure about using it the right way. I created the following snippet and expected something different. I expected that the item will be cancelled after calling cancel on it. But the iteration continues for some reason. Any ideas what I am doing wrong? The code seems fine for me.

@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()     } } 
like image 434
Sebastian Boldt Avatar asked Jul 14 '16 09:07

Sebastian Boldt


People also ask

How do you cancel a task in GCD Swift?

In your comments, you mention that you were using NSTimer , a.k.a. Timer in Swift. If you want to stop a timer, call timer. invalidate() to stop it. Create a new NSTimer when you want to start it again.

How do I cancel a dispatch work item?

A dispatch work item has a cancel flag. If it is cancelled before running, the dispatch queue won't execute it and will skip it. If it is cancelled during its execution, the cancel property return True. In that case, we can abort the execution.

How do I cancel Dispatchqueue?

You don't stop the queue. Instead, when the task that you dispatched starts executing, it needs to check its context and do the right thing (often: nothing) if the context has changed.

What is DispatchWorkItem?

Overview. A DispatchWorkItem encapsulates work to be performed on a dispatch queue or within a dispatch group. You can also use a work item as a DispatchSource event, registration, or cancellation handler.


1 Answers

GCD does not perform preemptive cancelations. So, to stop a work item that has already started, you have to test for cancelations yourself. In Swift, cancel the DispatchWorkItem. In Objective-C, call dispatch_block_cancel on the block you created with dispatch_block_create. You can then test to see if was canceled or not with isCancelled in Swift (known as dispatch_block_testcancel in 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 ?? true { break }             print(i)             self?.heavyWork()         }         item = nil    // resolve strong reference cycle of the `DispatchWorkItem`     }      // start it      queue.async(execute: item!)      // after five seconds, stop it if it hasn't already      queue.asyncAfter(deadline: .now() + 5) {         item?.cancel()         item = nil     } } 

Or, in 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); }     }); } 
like image 151
Rob Avatar answered Sep 17 '22 18:09

Rob