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

31,828

Solution 1

To suspend a dispatch queue, it is simply queue.suspend() (dispatch_suspend(queue) in Objective-C). 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 is queue.resume() (or dispatch_resume(queue) in Objective-C). 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 DispatchQueue object that you created (or the dispatch_queue_t object that you created when you called dispatch_queue_create() in Objective-C).

In terms of canceling tasks queued on dispatch queues, this is a was introduced in iOS 8. One can item.cancel() a DispatchWorkItem (dispatch_block_cancel(block) a dispatch_block_t object in Objective-C). 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 item.isCancelled (or dispatch_block_testcancel() in Objective-C).

See https://stackoverflow.com/a/38372384/1271826 for examples on canceling dispatch work items.

If you want to cancel tasks, you might also consider using operation queues, i.e. OperationQueue (NSOperationQueue in Objective-C). 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. 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 “dispatch source timers” do this far more gracefully. 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.

Solution 2

You can't pause / cancel when using a GCD queue. If you need that functionality (and in a lot of general cases even if you don't) you should be using the higher level API - NSOperationQueue. This is built on top of GCD but it gives you the ability to control how many things are executing at the same time, suspend processing of the queue and to cancel individual / all operations.

Share:
31,828
lakshmanbob
Author by

lakshmanbob

Updated on August 06, 2021

Comments

  • lakshmanbob
    lakshmanbob over 2 years

    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

  • Rob
    Rob about 9 years
    I agree with the NSOperationQueue advice, but on the technical matter, iOS 8 added cancellation capabilities of GCD. See Power, Performance and Diagnostics: What's new in GCD and XPC.
  • Wain
    Wain about 9 years
    Thanks for the info @Rob, I'll need to take a look at that
  • ipmcc
    ipmcc about 9 years
    NB: dispatch_cancel is useful but, like every other cancellation idiom on these non-garbage-collected platforms, it cannot forcibly cancel an operation already in flight. It can only prevent a block from starting (i.e. remove it from the queue). If you want mid-air cancelation, you have to periodically check a flag and return early. NSOperation helps out with that to some degree, by providing the flag. There is no silver bullet for forcible cancellation.
  • lakshmanbob
    lakshmanbob about 9 years
    I was running one background thread using the NSTimer class get the all notification count. So we need stop some screens this task executing. We add this thread in queue. We write dispatch_suspend(self.queue); but the Queue is not stopping again and again.
  • Rob
    Rob about 9 years
    There are tons of things it could be. It's impossible to diagnose on the basis of the limited information provided. You should create the simplest possible example that manifests your problem and then edit your original question, including it there. We cannot help you if we cannot reproduce the problem.
  • damianostre
    damianostre over 3 years
    It seems you're explaining how to suspect a dispatch queue. The question's title, however, is about suspending a task that's been run under control of GCD. And that's what I like to know. I.e, when I'd use dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIO‌​RITY_DEFAULT, 0), and then have a long-running task in that block, how do I pause it, and later resume it from another running task? Or isn't that possible with GCD? They're POSIX threads, aren't they, so what about pthread_mutex_lock etc?
  • Rob
    Rob over 3 years
    Above, I outline the interfaces that GCD provides, namely the suspending/resuming of queues (though only custom queues, not global queues), and the canceling of tasks (with all the caveats I outlined above). There is no GCD interface for suspending a task. You’ll have to implement that yourself or refactor your code to adopt cancelation pattern where you can start a new task to pick up from where it left off.