iOS Ensuring a Function Is Called Only Once Per Session - ios

I have several threads and once they are all finished working, I need to call a myMergeBlock method exactly once per action. I can't use dispatch_once because I want to be able to call myMergeBlock at a later time.
Some pseudo code looks like this but is not yet thread safe:
BOOL worker1Finished, worker2Finished, worker3Finished;
void (^mergeBlock)(void) = ^{
if (worker1Finished && worker2Finished && worker3Finished)
dispatch_async(queue, myMergeBlock); // Must dispatch this only once
}
void (^worker1)(void) = ^{
...
worker1Finished = YES;
mergeBlock();
}
void (^worker2)(void) = ^{
...
worker2Finished = YES;
mergeBlock();
}
void (^worker3)(void) = ^{
...
worker3Finished = YES;
mergeBlock();
}
Also, based on the way the workers are called, I do not call them directly, but instead pass them into a function as arguments.

You want to use dispatch groups. First you create a group, schedule the three workers in the group, then add a notification block to the group.
It should look something like this:
//create dispatch group
dispatch_group_t myWorkGroup = dispatch_group_create();
//get one of the global concurrent queues
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
//submit your work blocks
dispatch_group_async(myWorkGroup, myQueue, worker1);
dispatch_group_async(myWorkGroup, myQueue, worker2);
dispatch_group_async(myWorkGroup, myQueue, worker3);
//set the mergeBlock to be submitted when all the blocks in the group are completed
dispatch_group_notify(myWorkGroup, myQueue, mergeBlock);
//release the group as you no longer need it
dispatch_release(myWorkGroup);
You could hang on the the group and reuse it later if you prefer. Be sure to schedule the work before the notification. If you try to schedule the notification first it will be dispatched immediately.
I haven't tested this code but I do use dispatch_groups in my projects.

This sounds very messy and low level. Have you looked at Operation Queues and Dispatch Groups and semaphores as discussed in the Concurrency Programming Guide. I think they may offer simpler solutions to your problem.

If you're targeting Lion or iOS 5 and up, you can use barrier blocks as long as the blocks are dispatched on a non-global, concurrent queue. For example:
dispatch_queue_t customConcurrentQueue = dispatch_queue_create("customQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(customConcurrentQueue, worker1);
dispatch_async(customConcurrentQueue, worker2);
dispatch_async(customConcurrentQueue, worker3);
dispatch_barrier_async(customConcurrentQueue, mergeBlock);
//Some time later, after you're sure all of the blocks have executed.
dispatch_queue_release(customConcurrentQueue);
A barrier block executes after all previously submitted blocks have finished executing, and any blocks submitted after the barrier block will be forced to wait until the barrier block has finished. Again, for reasons which should be obvious, you can't use barrier blocks on the global queues. You must create your own concurrent queue.

Related

Updating UI element without dispatching to the main queue

I have a dispatch_block_t which is passed to another function, and this block will be called when the function finishes the asynchronous task. But the problem is that I don't know which thread this block will be called.
I want to update my UI in the main thread, hence I want to use
dispatch_async(dispatch_get_main_queue(), ^{...})
to update my UI. But I am afraid that this will cause a deadlock if such occasion happens
dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
dispatch_async(queue, ^{
// outer block is waiting for this inner block to complete,
// inner block won't start before outer block finishes
// => deadlock
});
// this will never be reached
});
Is there a way to prevent the deadlock? Like updating the UI element without using the dispatch queue. Is it possible to create a weak reference to self in order to update the UI?
Try running your example with NSLogs and you'll notice that deadlock doesn't occur. This is due to the fact that using dispatch_async just submits a block to the queue without waiting for it to finish execution (in contrary to dispatch_sync).
So running this code:
dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(#"1");
dispatch_async(queue, ^{
NSLog(#"2");
});
NSLog(#"3");
});
Will produce the following log:
Testtt[32153:2250572] 1
Testtt[32153:2250572] 3
Testtt[32153:2250572] 2
Moreover, I'm concerned that using dispatch_async(dispatch_get_main_queue(), ^{...}) here is commonly-used technique which ensures that the consumer gets the result on the main thread (i.e. consumer doesn't 'care' about threading).
Why, though, you use dispatch_block_t to pass a completion block? In my opinion, it's a bit confusing to use something like that on the consumer side - I would pass an anonymous (without typedef) block or create my own typedef for these simple completion blocks.

Heavy calculations on a background thread in getter

I need to perform a lot of calculations every time a getter is called from my app. The data returned from the getter is constantly changing based on the environment, and it has to do a lot of calculations to compute what it should return. Therefore, I don't want the code in the getter running on the main thread. This is what I have so far:
#interface Calculator ()
#property (nonatomic, strong) dispatch_queue_t calculationThread;
#end
- (dispatch_queue_t)calculationThread {
if (!_calculationThread) {
_calculationThread = dispatch_queue_create("calculation_thread", NULL);
}
return _calculationThread;
}
- (NSArray *)calculation {
// perform calculation in calculationThread, which should not be on main thread and be asynchronous
return arrayContainingCalculations;
}
I basically want to know how to use GCD to replace the comment. I have tried using dispatch_queue_t and dispatch_group_notify, but I don't seem to be implementing it correctly.
I think using a callback is probably the simplest and most efficient solution to this problem.
It is simply impossible to use only a single getter to do an asynchronous calculation without blocking the thread it was called on, as you expect code called after it to continue executing while it does the calculation.
You just have to create a new method with a callback, for example:
-(void) doCalculation:(void(^)(NSArray* result))callback {
dispatch_async(self.calculationQueue, ^{
NSArray* result = self.calculation; // make sure this is doing a synchronous calculation. If it's asynchronous, you'll have to use a semaphore (or another callback!).
if (callback) {
dispatch_async(dispatch_get_main_queue(), ^{ // return to main thread
callback(result);
});
}
});
}
Then you can simply invoke it on your main thread like so:
[calculator doCalculation:^(NSArray* result) {
textView.text = [result[0] stringValue]; // update UI with new info.
}];
That way you can easily keep your resulting code in-line with the call to the method.
It's also worth noting that your calculationQueue's getter (I renamed it, as the word thread is misleading when you're working with queues) isn't thread-safe. I would advise you use a dispatch_once to make it thread-safe:
-(dispatch_queue_t) calculationQueue {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_calculationQueue = dispatch_queue_create("calculation_queue", DISPATCH_QUEUE_SERIAL);
});
return _calculationQueue;
}
You can use the following to put it on your queue asynchronously. The problem however is that the method is going to return immediately.
dispatch_async(your_queue, ^{
// Code to be executed on background thread
});
What you probably want is to have some kind of method calculateWithCompletion where the caller can define a block that you can invoke once the completion is finished.
As you said in your comment to Peter, you want to keep it so you can call self.calculation and get your logic executed and return the calculation synchronously.
However because you want to avoid locking the UI while this logic is executing, you would like it to execute on a background thread.
Therefore, all you should need to do is use dispatch_sync instead of dispatch_async inside of your calculate method.
What dispatch_sync does is it places a task (the block that contains your logic) onto a specified queue (probably should pick a global concurrent queue), which then executes your task on a thread the OS picks for you (not the main thread). dispatch_async does the same, Except that dispatch_async will continue execution immediately after dispatching your task onto a queue.
dispatch_sync on the other hand, will block execution in the current run loop until your tasks returns.
This will allow you to execute your expensive logic on a background thread, while still remaining synchronous so that you can continue using self.calculation

Delay in executing method after dispatch group operations are complete in iOS

I am calling a method after after the queues in my dispatch group complete executing. However, there is a significant delay in executing the final method even after all the queues have been executed. Can anyone explain any probable reasons?
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue,^{
//some code
}
dispatch_group_notify(group, queue,
^{
[self allTasksDone];
});
What I meant was that the method allTasksDone is executed after some delay even when the operation in the async queue has completed.
How does -allTasksDone work? If it's communicating with the user by updating user interface elements, it need to run on in the main thread's context, or else it'll appear that the UI elements in question are "delayed" -- they won't update until the main run loop happens to make them update.
Try this instead:
dispatch_group_notify(group, dispatch_get_main_queue(),
^{
[self allTasksDone];
});
As it is, you're running -allTasksDone on the default background queue, which doesn't play nice with AppKit or UIKit.
I suggest an alternative approach although you can most certainly accomplish this using dispatch groups.
// Important note: This does not work with global queues, but you can use target queues to direct your custom queue to one of your global queues if you need priorities.
dispatch_queue_t queue = dispatch_queue_create("com.mycompany.myqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,^{
//some code
}
dispatch_barrier_async(queue,
^{
// this executes when all previously dispatched blocks have finished.
[self allTasksDone];
});

`[NSThread isMainThread]` always returns YES

This code
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(#"Main Thread? %d", [NSThread isMainThread]);
});
shows that I'm in the main thread. Even doing this:
queue = dispatch_queue_create("nonMainQueue", NULL);
still reports that I'm in the main queue. This is, it seems, because I'm using dispatch sync.
Does this mean that my code is the same as not using dispatch_sync at all? Also: what's the point of dispatch_sync if it does nothing at all, then?
Because queues are not threads, in order to check if you are on the main 'queue', you must use different code, something similar to this:
if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
NSLog(#"On Main Thread!");
}
Just note that dispatch_get_current_queue is deprecated, and is subject to be completely removed in a later iOS/Mac OS version.
This is documented behavior. As an optimization the blocks passed to dispatch_sync are executed on the current thread if possible (which is almost always).
My understanding from Apple's GCD guide, there is no guarantee that dispatch queues will execute on a separate thread. GCD will determine which thread, and if necessary create a new one.
Part of the point is now you do not have to think about threads.
The only thing to keep in mind, is to make sure you are updating UI elements on the main queue, for example:
// on background queue
dispatch_async( dispatch_get_main_queue(), ^(void){
someLabel.text = #"My Text";
});

wait for GCD async to stop before new code

I have a GCD that goes on the background. I have a button that when pressed I want it to load a loading wait screen while the GCD finishes, and then execute the rest of the code on that button. Attached is the sample.
Mine does not work, I basically want to say, wait as long as it takes to finish GCD, and load a waiting message in the meantime, when done continue code.
Thank you
- (IBAction)btnTapped:(id)sender
{
shouldCancel=NO;
dispatch_queue_t existingQueque = dispatch_get_main_queue();//finds the current GCD, the one I created in a different method
dispatch_group_t group =dispatch_group_create();
dispatch_group_async(group, existingQueque, ^
{
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//does not work, I guess group can't be created here.
[self performSelectorOnMainThread:#selector(showWaitViewWithMessage:) withObject:#"Loading" waitUntilDone:YES];//load this until GCD queque done
[self performSelector:#selector(performSearch) withObject:nil afterDelay:0];
});
}
A couple of thoughts:
You suggest that dispatch_get_main_queue() "finds the current GCD, the one I created in a different method". No, this just gets the main queue (the one that, if you use it, will block your user interface), not the queue that you created elsewhere through dispatch_create_queue. The dispatch_get_main_queue() just gets the main queue, and while your searching is happening, your UI will be blocked (e.g. UIActivityIndicatorView won't spin, whatever).
If you've dispatched a whole bunch of tasks to a background queue, if you want to wait for all of them to finish, that's when you use dispatch_group_t or dispatch_barrier, but given what you've shown doesn't require that (you have only one dispatched operation), you just don't need to go there. By the way, barriers are not recommended if you're using global queues.
The typical pattern for a single GCD background task is more simple than your question suggests. You (a) update your UI to say "loading" and show a UIActivityIndicatorView or something like that, so the user has a richer UX showing them that the app is working on something; (b) dispatch the search in the background; and (c) when done, dispatch the UI update back to the main queue. Thus, the typical pattern is:
- (IBAction)btnTapped:(id)sender
{
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// or, if you've already created you own background queue just use that here,
// or just create one here. But don't use dispatch_get_main_queue, as that
// won't use a background queue.
//
// dispatch_queue_t backgroundQueue = dispatch_queue_create("org.yourdomain.yourapp.search", NULL);
[self showWaitViewWithMessage:#"Loading"];
dispatch_async(backgroundQueue, ^{
[self performSearch]; // do this in the background
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUiAfterSearch]; // when done, dispatch UI update back to main queue
});
});
// if you created a queue, remember to release it
//
// dispatch_release(backgroundQueue);
}
As an aside, in your performSelectorOnMainThread, I see no reason to waitUntilDone. Don't wait unless there is some compelling reason to do so. As you see above, this construct isn't needed at all, but just a FYI.
By the way, it's important to know that many servers impose limits on how many concurrent requests a given client may make at a time. If it's possible that you might be initiating multiple requests (e.g. the user taps buttons and the server is slow to respond) and this allows them to run concurrently. In this scenario, it's worth pursuing NSOperationQueue, where you can set maxConcurrentOperationCount. If you use the block versions of the NSOperationQueue methods (e.g. addOperationWithBlock rather than GCD's dispatch_async), the code can be structured in the same way, but it let's you constrain the number of background operations.
Also, NSOperationQueue offers the ability to easily establish dependencies between the operations (e.g. a completion NSOperation that is dependent on all of the others finishing). I can outline that, but the code you posted doesn't necessitate that, so I'll spare you that unless you let me know you want to see what that would look like.
you have to save the queue you create, dont create it each time and if you only want one at a time, use a serial queue
#implementation DDAppDelegate {
dispatch_queue_t queue;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self do];
[self performSelector:#selector(do) withObject:nil afterDelay:1];
}
- (void)do {
if(!queue)
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
//serialized
NSLog(#"1");
sleep(10);
});
}
#end
if you want a concurrent queue, use a global queue and dispatch_barrier_async
#implementation DDAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self do];
[self performSelector:#selector(do) withObject:nil afterDelay:1];
}
- (void)do {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_barrier_async(queue, ^{
//serialized
NSLog(#"1");
sleep(10);
});
}

Resources