I'm creating an application in objective C where I have two threads:
The main thread, which is woken up from sleep and is called into asynchronously by a module above it
The callback block(thread) whose execution is asynchronous and is dependent on an external module "M" sending a notification.
On my main thread, I want to wait for the callback to come in before I start doing my tasks. So, I tried using dispatch_group_enter and dispatch_group_wait(FOREVER) on the main thread while calling into dispatch_group_leave on the callback thread. This ensured that when the main thread is the first to execute, things happen as they are supposed to, i.e the main thread waits for the callback to come in and unblock it before performing its tasks.
However, I'm seeing a race condition where the callback block gets called first sometimes and is stuck on dispatch_group_leave (since at this point the main thread has not called into dispatch_group_enter.
Is there a different GCD construct I can use for this purpose?
The “main thread” is a thread which handles UI, system events, notifications, etc. We never block that thread. Blocking it results in a horrible UX where the app will appear to freeze and your app may even be terminated by the “watch dog” process, which kills apps that it thinks are frozen. In some cases, the app will deadlock.
So, if you really mean “main thread”, then the answer is that you would never “wait” on that thread (or otherwise block it). The pattern is to have your background thread do what it needs, and then dispatch model/UI updates back to the main thread with GCD (or submit your notification and let the main thread process it).
If you want a UX where the user is not allowed to interact with the UI while this background process is underway, you would present something in your UI that makes that clear. A common pattern is a dimming/blurring view that covers the whole view, often with a UIActivityIndicatorView (i.e., a spinner), and when the task dispatched to the background queue is done (or have the notification handler do that), you’d then remove that dimming/blurred view and the spinner and update the UI accordingly.
But you never block the main thread by waiting.
Related
Completion handler closures are common in iOS development, such as dataTask(with:completionHandler:) in the URLSession class.
The UI engine is managed by the main thread, the API calls by URLSession are run under the background thread and must be dispatched to the main thread if a UI update is needed in the handler.
Question 1:
Do all completion handler closures from the iOS framework run in the background thread?
Question 1.1:
Do all escaping closures, for example, created by developers, run in the background thread?
Question2:
I've seen up to 8 threads in the iPhone X simulator. Which one is the main thread and which one is the background thread in ios? Do they have different priorities and computational power?
Keep in mind that you are really asking about queues and threading more than completion handlers and closures. Code of any type is executed on a queue (which consists of one or more threads). There is nothing special about completion handler closures in this regard.
Q1 - Most iOS SDK provided completion handlers are called on a background queue but don't make that assumption unless the documentation specifically states what queue it is called on. Even URLSession can be configured to run on a specific queue, including the main queue.
Q1.1 - Closures that you write are run on whatever queue you call them from. There is no magic that makes them run on a background queue.
Q2 - The first thread is always the only thread of the main queue. All other threads are from background queues. Each thread can have whatever priority is was given based on the properties of its queue.
You should review the Dispatch documentation for further details, especially DispatchQueue.
I'm an Android engineer trying to port some iOS code that uses 5 SERIAL dispatch queues. I want to make sure I'm thinking about things the right way.
dispatch_sync to a SERIAL queue is basically using the queue as a synchronized queue- only one thread may access it and the block that gets executed can be thought of as a critical region. It happens immediately on the current thread- its the equivalent of
get_semaphore()
queue.pop()
do_block()
release_semaphore()
dispatch_async to a serial queue- performs the block on another thread and lets the current thread return immediately. However since its a serial queue it promises only one of these asynchronous thread is going to execute at a time (the next call to dispatch_async will wait until all other threads are finished). That block can also be thought of as a critical region, but it will occur on another thread. So the same code as above, but its passed to a worker thread first.
Am I off in any of that, or did I figure it out correctly?
This feels like an overly complicated way of thinking of it and there are lots of little details of that description that aren't quite right. Specifically, "it happens immediately on the current thread" is not correct.
First, let's step back: The distinction between dispatch_async and dispatch_sync is merely whether the current thread waits for it or not. But when you dispatch something to a serial queue, you should always imagine that it's running on a separate worker thread of GCD's own choosing. Yes, as an optimization, sometimes dispatch_sync will use the current thread, but you are in no way guaranteed of this fact.
Second, when you discuss dispatch_sync, you say something about it running "immediately". But it's by no means assured to be immediate. If a thread does dispatch_sync to some serial queue, then that thread will block until (a) any block currently running on on that serial queue finish; (b) any other queued blocks for that serial queue run and complete; and (c) obviously, the block that thread A itself dispatched runs and completes.
Now, when you use a serial queue for synchronization for, some thread-safe access to some object in memory, often that synchronization process is very quick, so the waiting thread will generally be blocked for a negligible amount of time as its dispatched block (and any prior dispatched blocks) to finish. But in general, it's misleading to say that it will run immediately. (If it always could run immediately, then you wouldn't need a queue to synchronize access).
Now your question talks about a "critical region", to which I assume you're talking about some bit of code that, in order to ensure thread-safety or for some other reason like that, must be synchronized. So, when running this code to be synchronized, the only question re dispatch_sync vs dispatch_async is whether the current thread must wait. A common pattern, for example, is to say that one may dispatch_async writes to some model (because there's no need to wait for the model to update before proceeding), but dispatch_sync reads from some model (because you obviously don't want to proceed until the read value is returned).
A further optimization of that sync/async pattern is the reader-writer pattern, where concurrent reads are permissible but concurrent writes are not. Thus, you'll use a concurrent queue, dispatch_barrier_async the writes (achieving serial-like behavior for the writes), but dispatch_sync the reads (enjoying concurrent performance with respect to other read operations).
To pick nits, dispatch_sync doesn't necessarily run the code on the current thread, but if it doesn't, it still blocks the current thread until the task completes. The distinction is only potentially important if you're relying on thread IDs or thread-local storage.
But otherwise, yes, unless I missed something subtle.
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
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.
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.
They suggest:
When using GCD, use a dedicated serial queue to dispatch commands to
OpenGL ES; this can be used to replace the conventional mutex pattern.
I don't understand this recommendation. There is this conflict that I cannot solve:
When the app delegate of an app receives the -applicationWillResignActive call, it must immediately stop calling any OpenGL function.
If the app continues to call an OpenGL function after -applicationWillResignActive returned, the app will crash.
If I follow Apple's recommendation to call OpenGL functions in a serial background queue, I am faced with this seemingly unsolvable problem:
1) After I receive -applicationWillResignActive I must immediately stop calling any further OpenGL functions.
2) But because the serial queue is in the middle of processing a block of code in the background, sometimes the block of code would finish executing after -applicationWillResignActive returns, and the app crashes.
Here is an illustration showing the concurrent "blocks". The main thread receives a full stop message and has to prevent further calls to OpenGL ES. But unfortunately these happen in a background queue which cannot be halted in flight of working on a block:
|_____main thread: "STOP calling OpenGL ES!"_____|
_____|_____drawing queue: "Draw!"_____|_____drawing queue: "Draw!"_____|
Technically I found no way to halt the background queue instantly and avoid further calls to OpenGL in the background. A submitted block of code once running keeps running.
The only solution I found was to NOT call OpenGL ES functions in the background. Instead, call them on the main thread to guarantee that they will never be called after the app lost access to the GPU.
So if it is okay to call OpenGL ES functions in the background, how do you ensure that they never get called after the app resigned active?
Just wait in applicationWillResignActive for the queue to finish all enqueued operations using a dispatch group or a similar mechanism.
You can find an example in the documentation:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Release the group when it is no longer needed.
dispatch_release(group);
Beyond Sven's suggestion of using a dispatch group, I've gone a simpler route in the past by using a synchronous dispatch into your rendering serial queue within -applicationWillResignActive:
// Tell whatever is generating rendering operations to pause
dispatch_sync(openGLESSerialQueue, ^{
[EAGLContext setCurrentContext:context];
glFinish();
// Whatever other cleanup is required
});
The synchronous dispatch here will block until all actions in the serial queue have finished, then it will run your code within that block. If you have a timer or some other source that's triggering new rendering blocks, I'd pause that first, in case it puts one last block on the queue.
As an added measure of safety, I employ glFinish() which blocks until all rendering has finished on the GPU (the PowerVR GPUs like deferring rendering as much as they can). For extremely long-running rendered frames I've occasionally seen crashes due to frame rendering still going even after all responsible OpenGL ES calls have finished. This prevents that.
I use this within a few different applications, and it pretty much eliminates crashes due to rendering when transitioning to the background. I know others use something similar with my GPUImage library (which also uses a serial background dispatch queue for its rendering) and it seems to work well for them.
The one thing you have to be cautious of, with this or any other solution that blocks until a background process is done, is that you open yourself up to deadlocks if you're not careful. If you have any synchronous dispatches back to the main queue within blocks on your background queue, you're going to want to remove those or try to make them asynchronous. If they're waiting on the main queue, and the main queue is waiting on the background queue, you'll freeze up quite nicely. Usually, it's pretty straightforward to avoid that.
I have a method in my AppDelegate that saves a NSManagedObjectContext passed as a parameter. This method may be called on either the main thread or a background thread, and I want to ensure that when this method is called the main thread doesn't kill it halfway through when the user terminates the program or what have you. How do I prevent the main thread from killing this thread when this method is being executed?
I also want to do this for writing data to a plist file -- this also may happen outside of the main thread.
I am doing all my background work by way of GCD and dispatch_async
Brad Larson had some suggestions on this post that should help you.
In your appDelegate, when your app is getting pushed to the background, it calls applicationDidEnterBackground. From this routine, you can see if your method is currently in progress, and can behave accordingly.
Depending on how long your task takes to run there are two options:
If it will take significantly less than 5 seconds, you can just have your applicationDidEnterBackground wait until your task is completed before releasing. If you take longer than 5 seconds, your app will just get force-killed.
If it could be a while, you can try beginBackgroundTaskWithExpirationHandler: to start a background task that finishes off your task.