How to stop/cancel/suspend/resume tasks on GCD queue

lakshmanbob picture lakshmanbob · Apr 7, 2015 · Viewed 28.2k times · Source

How to stop/cancel/suspend/resume tasks on GCD queue

How does one stop background queue operations? I want to stop some screens in our app. And some screens it should be auto resume. So, how does one pass a queue in iOS?

I mean when user have browsing the app time we run the background thread in dispatch_queue_t. But it never stops and resume in the code. So how does one suspend and resume a queue

Answer

Rob picture Rob · Apr 7, 2015

To suspend a dispatch queue, it's simply dispatch_suspend(queue) in Objective-C or Swift 2.3, or queue.suspend() in Swift 3. That doesn't affect any tasks currently running, but merely prevents new tasks from starting on that queue. Also, you obviously only suspend queues that you created (not global queues, not main queue).

To resume a dispatch queue, it's dispatch_resume(queue) in Objective-C or Swift 2.3, or queue.resume() in Swift 3. There's no concept of "auto resume", so you'd just have to manually resume it when appropriate.

To pass a dispatch queue around, you simply pass the dispatch_queue_t object that you created when you called dispatch_queue_create() in Objective-C or Swift 2.3, or, in Swift 3, the DispatchQueue object you create with DispatchQueue(label:).

In terms of canceling tasks queued on dispatch queues, this is a new feature of iOS 8 and you'd call dispatch_block_cancel(block) with your dispatch_block_t object in Objective-C or Swift 2.3, or item.cancel() of a DispatchWorkItem in Swift 3. This cancels queued blocks/items that have not started, but does not stop ones that are underway. If you want to be able to interrupt a dispatched block/item, you have to periodically examine dispatch_block_testcancel() in Objective-C and Swift 2.3, or item.isCancelled in Swift 3.

The canceling of GCD blocks is sufficiently complicated that I might refer you to the WWDC 2014 video Power, Performance and Diagnostics: What's new in GCD and XPC, which introduces you to the concept of dispatch block objects, queuing them, canceling them, etc. While this is describing the older Objective-C and Swift 2.3 API, all of the concepts are equally applicable to the newer Swift 3 API.

If you want to cancel tasks, you might also consider using operation queues, NSOperationQueue in Objective-C or Swift 2.3, a.k.a. OperationQueue in Swift 3, as its cancelable operations have been around for a while and you're likely to find lots of examples online. It also supports constraining the degree of concurrency with maxConcurrentOperationCount (whereas with dispatch queues you can only choose between serial and concurrent, and controlling concurrency more than that requires a tiny bit of effort on your part).

If using operation queues, you suspend and resume by changing the suspended property of the queue. And to pass it around, you just pass the NSOperationQueue object you instantiated.


Having said all of that, I'd suggest you expand your question to elaborate what sort of tasks are running in the background and articulate why you want to suspend them. There might be better approaches than suspending the background queue.


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

Or if the timer is really running on a background thread, GCD timers do this far more gracefully, avoiding the silliness of needing to create a runloop on a background thread, which you need to run NSTimer on background threads. With a GCD timer, you can suspend/resume it just like you suspend/resume a queue, just using the timer object instead of the queue object.