iOS-Managing and Keeping Track of Multiple Concurrent tasks - ios

In my app I need to load up data from multiple sources and put them together in a table view. Gathering each of the sources one after another would take forever. To get around this I need to run all of the download operations together. Since they are download tasks, in theory I could just run them, but the issue is that only part of the code on the thread runs asynchronously, which means it will need the main thread to complete the operation.
So in order to get ALL of it running in the background, I need to use GCD, which I don't have much experience with.
//DataLoader.m
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[self.webLoader getFeedWithCompletion:self.thatOtherCompletionBlock];
[self.otherDataLoader getDataWithCompletion:self.completionBlock];
[self.thatDataLoader getThatDataWithCompletion:self.anotherCompletionBlock]
dispatch_async(dispatch_get_main_queue(), ^(void){
});
});
However, since part of the task is already asynchronous, I need to figure out where to put GCD code.
I could put it before starting the task, like I did above. This could work, however, since the tasks are already partially run in the background (in some cases I cannot change that), it seems wasteful to be running a task that already runs partially in the background in the background. Why run something that already runs in a background thread in another thread?
Another option would be to use GCD in the actual class that gets the feed (ex. webloader), putting it on all code that isn't running in the background
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
.......
});
Which way is better?
There is also another problem. Since part of the tasks are asynchronous, they use completion blocks. Not only do I need to also run the completion blocks in the background, I need to figure out which one is the last one to finish, so I can run some code to clean up and neatly package and ship the data to the view controller.
The way I thought of would be to use a BOOL for each task, simply changing it to true when it's done. Then in my completion blocks I can check if all the other tasks are complete, and if so, run the clean up code. However, this may not be the most elegant solution.
What would be the best way to deal with these tasks, ensuring that it all happens in the background?

GCD groups could easily be used for this. Groups allow you to track arbitrary "members" of the group, and hook a block up to run when all members of the group have finished. It's quite handy. For example (using your code):
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group); // + 1
[self.webLoader getFeedWithCompletion: ^{
self.thatOtherCompletionBlock();
dispatch_group_leave(group); // - 1
}];
dispatch_group_enter(group); // + 1
[self.otherDataLoader getDataWithCompletion:^{
self.completionBlock();
dispatch_group_leave(group); // - 1
}];
dispatch_group_enter(group); // + 1
[self.thatDataLoader getThatDataWithCompletion:^{
self.anotherCompletionBlock();
dispatch_group_leave(group); // - 1
}];
dispatch_group_notify(group, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// This will get executed once all three of the prior completion blocks have been run.
// i.e. when the group "count" goes to zero.
});
dispatch_release(group);
});
You could also, albeit a bit circuitously, use NSOperation's inter-operation dependency feature to achieve this. Like this:
NSOperationQueue* q = [[[NSOperationQueue alloc] init] autorelease];
NSOperation* completionA = [NSBlockOperation blockOperationWithBlock: self.thatOtherCompletionBlock];
NSOperation* completionB = [NSBlockOperation blockOperationWithBlock: self.completionBlock];
NSOperation* completionC = [NSBlockOperation blockOperationWithBlock: self.anotherCompletionBlock];
NSBlockOperation* afterAllThree = [[[NSBlockOperation alloc] init] autorelease];
[afterAllThree addDependency: completionA];
[afterAllThree addDependency: completionB];
[afterAllThree addDependency: completionC];
[afterAllThree addExecutionBlock:^{
// This will get executed once all three of the prior completion blocks have been run.
}];
// Kick off the tasks
[q addOperationWithBlock:^{
[self.webLoader getFeedWithCompletion: ^{ [q addOperation: completionA];}];
[self.otherDataLoader getDataWithCompletion:^{ [q addOperation: completionB]; }];
[self.thatDataLoader getThatDataWithCompletion:^{ [q addOperation: completionC]; }];
}];
I personally prefer the dispatch_group method, but they would both get the job done.

Related

Issue in execute a method after completion of another method - iOS

I have two methods as loadTopicPostsFromDB and loadTopicPosts. In the loadTopicPostsFromDB method I am updating the value of a global NSString called strLastTimeStamp which should use in the loadTopicPosts. Thus, I want to execute loadTopicPostsFromDB first and after it finished(global string updated) I want to execute loadTopicPosts method.
This is how I did it. But, currently loadTopicPosts method executes before updating the global strLastTimeStamp, so always I get a wrong strLastTimeStamp.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self performSelectorOnMainThread:#selector(loadTopicPostsFromDB) withObject:nil waitUntilDone:NO];
});
dispatch_group_notify(group, queue, ^{
NSLog(#"LoadDBCompleted");
[self loadTopicPosts];
});
How can I do this, please advice me on what is the wrong in this implementation.
performSelectorOnMainThread: is finished as soon as iOS has put the task into a queue. The selector has most likely not even started running when the call returns. And really, you shouldn't be using performSelectorOnMainThread at all - the function isn't available in Swift, for good reason. The solution is a lot easier (fix the problems yourself):
dispatch_async (dispatch_get_main_queue (), ^{
[self loadTopicsFromDB];
[self loadTopicPosts];
});
You probably want to perform loadTopicsFromDB on a background thread though.
When you are doing something using network connection I advice you to use blocks to handle the endpoint of the call.
It is pretty simple to write in this code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self loadTopicsFromDB: ^(BOOL success, NSError *error) {
[self loadTopicPosts];
}];
});

Executing Methods One After Another in iOS [duplicate]

This question already has answers here:
How do I perform several methods in sequence?
(2 answers)
Closed 8 years ago.
In my application I have four methods like
- (void)Method1;
- (void)Method2;
- (void)Method3;
- (void)Method4;
I want to execute these methods one after another. I search for this one in some sources they are using "dispatch_Time" in some sources they are using "NSThread sleepForTimeInterval:" But in my application I don't want to execute those methods using time. I want to execute them if the previous method execution is completed. How can I do this?
Solution Overview
The right way of doing this is to enqueue the methods onto a serial queue.
A serial queue executes one task at a time and thus ensures that a task is only executed once all it's predecessors were executed.
There are several ways of accomplishing what you wish. I will describe 2 of them, one using Grand Central Dispatch and one using NSOperationQueue.
Grand Central Dispatch
Create a queue onto you will enqueue your task. A good practice would be to keep the queue as an instance variable so that you could access it from instance methods (unlike NSOperationQueue, by default custom dispatch queues are serial by default, i.e. they execute one task at a time):
dispatch_queue_t my_queue = dispatch_queue_create("com.suresh.methodsqueue", NULL);
self.methods_queue = my_queue;
Enqueue your methods onto the designated queue one after the other:
dispatch_async(self.methods_queue, ^{ [someObject method1] });
dispatch_async(self.methods_queue, ^{ [someObject method2] });
dispatch_async(self.methods_queue, ^{ [someObject method3] });
dispatch_async(self.methods_queue, ^{ [someObject method4] });
Further re GCD in Apple's developer guides.
Operation Queue
Initialize a queue:
NSOperationQueue* aQueue = [[NSOperationQueue alloc] init];
self.methods_queue = aQueue;
Make sure the queue is serial:
[self.methods_queue setMaxConcurrentOperationCount:1]
Enqueue the methods onto the queue, there's several ways of doing so, the following requires the least amount of code:
[self.methods_queue addOperationWithBlock:^{ [someObject method1] }];
[self.methods_queue addOperationWithBlock:^{ [someObject method2] }];
[self.methods_queue addOperationWithBlock:^{ [someObject method3] }];
[self.methods_queue addOperationWithBlock:^{ [someObject method4] }];
Further re Operation Queues in Apple's developer guides.
If the methods don't return until they've finished their work, just call them in order:
[self Method1];
[self Method2];
[self Method3];
[self Method4];
Otherwise, you need to use something like dispatch groups.
call first method somewhere like
[self (void)Method1];
and implement like
(void)Method1{
// logic here
[self (void)Method2];
}
(void)Method2{
// logic here
[self (void)Method3];
}
and so on ...............

Intangible Order of Execution (dispatch_semaphore_t, dispatch_group_async) and the Use of Them in Combination with Different Dispatch Queue Types

I just took some time in the evening to play around with GCD, especially with dispatch_semaphore_t because I never used it. Never had the need to.
So I wrote the following as a test:
- (void)viewDidLoad
{
UIView *firstView = [[UIView alloc] initWithFrame:(CGRect){{0, 0}, self.view.frame.size.width/4, self.view.frame.size.width/5}];
firstView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:firstView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
for (long i = 0; i < 1000; i++)
{
sleep(5);
dispatch_async(dispatch_get_main_queue(), ^
{
firstView.layer.opacity = ((i%2) ? 0: 1);
});
}
});
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group1 = dispatch_group_create();
dispatch_group_async(group1, queue1, ^
{
sleep(3);
NSLog(#"dispatch group 1");
});
dispatch_group_notify(group1, queue1, ^
{
NSLog(#"dispatch notify 1");
});
dispatch_async(myQueue, ^
{
for(int z = 0; z < 10; z++)
{
NSLog(#"%i", z);
sleep(1);
}
dispatch_semaphore_signal(mySemaphore);
});
dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);
NSLog(#"Loop is Done");
}
If I ran the above, the output would be:
0
1
2
dispatch group 1
dispatch notify 1
3
4
5
6
7
8
9
Loop is Done
After the above, firstView appears on the screen (before semaphore the whole screen was black) and finally this gets executed:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
for (long i = 0; i < 1000; i++)
{
sleep(5);
dispatch_async(dispatch_get_main_queue(), ^
{
firstView.layer.opacity = ((i%2) ? 0: 1);
});
}
});
Which only alternates the opacity as the loop runs after semaphore is done.
1.)
So, it seems that I have to wait until dispatch_semaphore finish to do its work before any UI thing takes place.
BUT:
It seems like dispatch_group_t runs concurrently with dispatch_semaphore as shown from the output above (i.e., 1, 2, 3, ....).
???
2.)
And if I change the for loop in the above to using: dispatch_async(dispatch_get_main_queue(), ^
instead of:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^,
nothing gets shown on the screen even after semaphore is done.
How so???
3.)
Furthermore if I change semaphore to the following as opposed to using a global queue like in the above:
dispatch_async(dispatch_get_main_queue(), ^
{
for(int z = 0; z < 10; z++)
{
NSLog(#"%i", z);
sleep(1);
}
dispatch_semaphore_signal(mySemaphore);
});
Only dispatch_group takes place; nothing else take places / get executed, not the for loop in the above, including UI. Nothing.
4.)
So besides what I pointed out in the above, what can I do, in order to make semaphore not blocking my UI and my other process and just let my UI and the other processes do their thing?
And as mentioned above, why changing the type of queue for semaphore from global to main would cause nothing to be shown on screen and even the loop would not execute except dispatch_group?
5.)
If I change semaphore to:
dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(1);
//1 instead of 0 (zero)
Everything (i.e., both for loop and UI) runs immediately and NSLog(#"Loop is Done"); is displayed also immediately, which tells me that semaphore didn't wait here:
dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);
NSLog(#"Loop is Done");
???
I spent the whole evening trying to figure this out, but to no avail.
I hope someone with great GCD knowledge can enlighten me on this.
First things first: As a general rule, one should never block the main queue. This rule about not blocking the main queue applies to both dispatch_semaphore_wait() and sleep() (as well as any of the synchronous dispatches, any group wait, etc.). You should never do any of these potentially blocking calls on the main queue. And if you follow this rule, your UI should never become non-responsive.
Your code sample and subsequent questions might seem to suggest a confusion between groups and semaphores. Dispatch groups are a way of keeping track of a group of dispatched blocks. But you're not taking advantage of the features of dispatch groups here, so I might suggest excising them from the discussion as it's irrelevant to the discussion about semaphores.
Dispatch semaphores are, on the other hand, simply a mechanism for one thread to send a signal to another thread that is waiting for the signal. Needless to say, the fact that you've created a semaphore and sent signals via that semaphore will not affect any of your dispatched tasks (whether to group or not) unless the code in question happens to call dispatch_semaphore_wait.
Finally, in some of your later examples you tried have the semaphore send multiple signals or changing the initial count to supplied when creating the semaphore. For each signal, you generally want a corresponding wait. If you have ten signals, you want ten waits.
So, let's illustrate semaphores in a way where your main queue (and thus the UI) will never be blocked. Here, we can send ten signals between two separate concurrently running tasks, having the latter one update the UI:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// send 10 signals from one background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(#"Sleeping %d", i);
sleep(3);
NSLog(#"Sending signal %d", i);
dispatch_semaphore_signal(semaphore);
}
NSLog(#"Done signaling");
});
// and on another thread, wait for those 10 signals ...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(#"Waiting for signal %d", i);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(#"Got signal %d", i);
// if you want to update your UI, then dispatch that back to the main queue
dispatch_async(dispatch_get_main_queue(), ^{
// update your UI here
});
}
NSLog(#"Done waiting");
});
This is, admittedly, not a terribly useful example of semaphores, but it illustrates how theoretically you could use them. In practice, it's rare that you have to use semaphores, as for most business problems, there are other, more elegant coding patterns. If you describe what you're trying to do, we can show you how to best achieve it.
As for an example with non-zero value passed to dispatch_semaphore_create, that's used to control access to some finite resource. In this example, let's assume that you had 100 tasks to run, but you didn't want more than 5 to run at any given time (e.g. you're using network connections (which are limited), or the each operation takes up so much memory that you want to avoid having more than five running at any given time). Then you could do something like:
// we only want five to run at any given time
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
// send this to background queue, so that when we wait, it doesn't block main queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 100; i++)
{
// wait until one of our five "slots" are available
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// when it is, dispatch code to background queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"starting %d", i);
// to simulate something slow happening in the background, we'll just sleep
sleep(5);
NSLog(#"Finishing %d", i);
// when done, signal that this "slot" is free (please note, this is done
// inside the dispatched block of code)
dispatch_semaphore_signal(semaphore);
});
}
});
Again, this isn't a great example of semaphores (in this case, I'd generally use an NSOperationQueue with a maxConcurrentOperationCount), but it illustrates an example of why you'd use a non-zero value for dispatch_source_create.
You've asked a number of questions about groups. I contend that groups are unrelated to your own semaphores. You might use a group, for example, if you want to run a block of code when all of the tasks are complete. So here is a variation of the above example, but using a dispatch_group_notify to do something when all of the other tasks in that group are complete.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // or create your own concurrent queue
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_group_t group = dispatch_group_create();
// send this to background queue, so that when we wait, it doesn't block main queue
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 100; i++)
{
// wait until one of our five "slots" are available
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// when it is, dispatch code to background queue
dispatch_group_async(group, queue, ^{
NSLog(#"starting %d", i);
// to simulate something slow happening in the background, we'll just sleep
sleep(5);
NSLog(#"Finishing %d", i);
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_notify(group, queue, ^{
NSLog(#"All done");
});
});

How to dispatch_group_wait for dispatch_group_async inside an asynchronous block

I have code that looks something like this:
[SVProgressHUD show];
[imageGenerator generateCGImagesAsynchronouslyForTimes:times
completionHandler:^(CMTime requestedTime, ...) {
dispatch_group_async(queueGroup, queue, ^{
// Do stuff
});
}];
dispatch_group_wait(queueGroup, DISPATCH_TIME_FOREVER);
[SVProgressHUD dismiss];
Basically, display a loading animation HUD and start generating image thumbnails from an asset, then once it's done hide the HUD. I'm using a dispatch group since i want to make sure all the thumbnails are generated before i hide the HUD.
But when i run it, the HUD gets dismissed immediately. I'm guessing this is because of the asynchronous nature of the generateCGImagesAsynchronouslyForTimes: completionHandler:--dispatch_group_wait is called before the first dispatch_group_async inside the completionHandler.
What is a graceful way to get around this situation? Thanks.
Think of this method as a static counter available to threads, so when you enter a group the counter increments, and when that block returns, decrements...
When that counter is 0, it will call a block to invoke
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
while(someCondition)
{
dispatch_group_enter(group);
[SomeClassThatLoadsOffTheInternet getMyImages:^{
// do something with these.
dispatch_group_leave(group);
});
}
dispatch_group_notify(group, queue, ^{
// do something when all images have loaded
});
Is that what you were thinking of?

How does performSelectorInBackground and detachNewThreadSelector work?

I need to perform an asynchronous function execution because it is blocking the main thread and hence the UI is not available.
After looking at the questions in stackoverflow, I know there are three ways to do asynchronous function.
An example:
[NSThread detachNewThreadSelector:#selector(showSpinner:) toTarget:self withObject:self.view];
// or
[self performSelectorInBackground:#selector(showSpinner:) withObject:self.view];
// or
NSInvocationOperation *invOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(showSpinner:) object:self.view];
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperation:invOperation];
// or
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self showSpinner:self.view];
});
});
My question is how does performSelectorInBackground and detachNewThreadSelector return back to main thread? how do you know that they are done?
A couple of thoughts:
You might want to check Migrating Away From Threads in the Concurrency Programming Guide, which makes a compelling argument for dispatch queues and operation queues, which are discussed earlier in that same guide.
Also, as you delve into various asynchronous operations, remember, do time consuming stuff in background queues/threads, but always dispatch UI stuff back to the main queue. I only mention that because your task, showSpinner sounds a lot like a UI task, which you would never want to do in a background queue/thread. If it has some "expensive" non-UI related tasks, then fine, do that in the background, but make sure the UI stuff gets dispatched back to the main queue.
There are, as an aside, other renditions of the operations queues, e.g., block operations:
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperationWithBlock:^{
// do some slow stuff in the background here
// ok, now do UI stuff in the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self showSpinner:self.view];
}];
}];
This is roughly equivalent to the GCD (dispatch queue) rendition:
dispatch_queue_t dispatchQueue = dispatch_queue_create("com.ramshad.app", 0);
dispatch_async(dispatchQueue, ^{
// do some slow stuff in the background here
// ok, now do UI stuff in the main queue
dispatch_async(dispatch_get_main_queue(), ^{
[self showSpinner:self.view];
});
});
There are tons of subtle pros and cons between the operation queues and dispatch queues (which we should not get into here because it's been discussed hundreds of times elsewhere on Stack Overflow), but both let you do surprisingly rich asynchronous operations with less complexity than traditional thread programming.
If you decide to stick with threads versus operation and/or dispatch queues (which I wouldn't necessarily recommend), you might want to check out the Threading Programming Guide.
To identify performSelectorInBackground & detachNewThreadSelector end of execution,call a method at the end of the thread method on main thread.
Additionaly NSThread provides an propery as isFinished which returns a Boolean value that indicates whether the receiver has finished execution.
Example:
[self performSelectorOnMainThread:#selector(threadMethod)
withObject:nil
waitUntilDone:NO];
or
[NSThread detachNewThreadSelector:#selector(threadMethod)
toTarget:self
withObject:nil];
-(void)threadMethod{
//here your implementation code
//here call the end notification method.
[self performSelectorOnMainThread:#selector(ThreadExecutionDone)
withObject:nil
waitUntilDone:YES];
}
-(void)ThreadExecutionDone{
//end of the performSelectorInBackground or detachNewThreadSelector.
}

Resources