I have the following For loop which iterates through an NSMutableArray and calls the setImage method :
//Code to iterate through pictures and create ImageView class for each one.
for (int i =0; i<=[pictureThumbnailArray count]-1; i++) {
NSLog(#"Thumbnail count is %d", [pictureThumbnailArray count]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
[self setImage:[pictureThumbnailArray objectAtIndex:i]:i];
});
}
The setImage method sets various parameters on the picture and then finally adds it to the subview on the main thread :
dispatch_sync(dispatch_get_main_queue(), ^(void) {
[self.view addSubview:onePicture];
});
The problem is that the images appear on the screen randomly, rather than loading in order one by one. Can any suggest how I can improve this ?
Thanks.
The global background queue is concurrent, not serial, meaning that it doesn't guarantee ordering. You can create a custom serial queue that does:
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
and use that in your dispatch_async.
Related
I've come to a problem where proper threading is needed, but I can't seem to optimise it correctly.
Here's my method:
-(void) method1
{
// -1 to an NSInteger
nsint1--;
[self showActiviyIndicator:YES]; //act as loading screen
[alloc database etc stuffs and retrieving of data here]
//for loop here to check with database, and grey out button depending on database values
for (int i = 1; i<12; i ++)
{
//get values from database and store into variables, then grey out the button if variables are 0.
}
int Val1 = [get from database]
if Val1 = 0
[button setTitleColor:[UIColor Grey]];
someLabel.text = [NSString stringWithFormat:#"%ld", (long)nsint1];
//here's where the problem lies
[self refreshTableSessionList:xx];
[self showActiviyIndicator:NO]
}
inside [self refreshTableSessionList:xx], there's a
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
to get data from server database, then a
dispatch_async(dispatch_get_main_queue(),
to populate and reload tableViewCell.
But there'll be a conflict for when I put a
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
before [alloc database etc stuffs and retrieving of data here]
and put dispatch_async(dispatch_get_main_queue(),
when greying out the button, but that's inside a loop, which i don't think it is the right way.
What's the solution to overcome this?
As I understood you don't wait for the finish of the background database stuff.
Have you read about multithreading? For example, Ray's article.
In a simple way, you can call dispatch_async inside the dipatch_async block inside the dispatch_async and etc.
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some database stuff
dispatch_async(dispatch_get_main_queue(), ^{
// do some UI stuff
});
});
So you should switch between the main thread and a global queue. Also, you can use delegates, notifications or even reactivity for such purposes.
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");
});
});
I want to illustrate the progress on MBProgressHUD item, but when i triger this method :
- (IBAction)signInBttn:(id)sender {
MBProgressHUD *hudd = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hudd.mode = MBProgressHUDModeAnnularDeterminate;
hudd.labelText = #"Loading";
__block float value = 0;
for (int j = 0; j<2000; j++) {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i<20000 ; i++) {
}
value += 0.001;
dispatch_async( dispatch_get_main_queue(), ^{
hudd.progress = value;
});
});
}
}
hud appears fully to 100%. This is only for my information, I dont have idea how to create background task which calculate something and when he done with e.g. 40% the HUD is refreshing to 40% of his progress. I hope I made myself clear, and if anyone has time to help improve my code, thanks a lot for any answers
In this case, you can solve the problem by decoupling the updating of the counter from the updating of your HUD in your UI. Apple refers to this as "updating the state asynchronously" in WWDC 2012 video Asynchronous Design Patterns with Blocks, GCD, and XPC.
Generally this isn't necessary (most of the time the stuff we're doing asynchronously is slow enough that we don't have problems), but if doing something that is running faster than the UI can hope to keep up with, you create a "dispatch source" for this. I'm going to illustrate it with a UIProgressView, but the same applies to pretty much any UI:
// create source for which we'll be incrementing a counter,
// and tell it to run the event handler in the main loop
// (because we're going to be updating the UI)
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
// specify what you want the even handler to do (i.e. update the HUD or progress bar)
dispatch_source_set_event_handler(source, ^{
self.iterations += dispatch_source_get_data(source);
[self.progressView setProgress: (float) self.iterations / kMaxIterations];
});
// start the dispatch source
dispatch_resume(source);
// now, initiate the process that will update the source
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (long i = 0; i < kMaxIterations; i++)
{
// presumably, do something meaningful here
// now increment counter (and the event handler will take care of the UI)
dispatch_source_merge_data(source, 1);
}
// when all done, cancel the dispatch source
dispatch_source_cancel(source);
});
In my example, iterations is just a long property:
#property (nonatomic) long iterations;
And I defined my kMaxIterations constant as follows:
static long const kMaxIterations = 10000000l;
First off, if you want to delay execution use dispatch_after: Apple Doc since it could be that Clang is optimizing your loop (i.e. by making it not exist).
Within that block call dispatch_sync on the main thread to update the UI, since dispatch_async is not guaranteed to execute 'evenly'. Something like this ought to work...
for (...) {
dispatch_after(<some formula of i>, DEFAULT_PRIORITY, ^{
dispatch_sync(MAIN_QUEUE, ^{ hudd.progress = value });
}
}
In my app, I have a UITableViewController.
Its tableView is divided in 3 sections.
I download datas for each of those sections from my server. To do this, I have 3 functions (for example f1 f2 and f3). Each updates a corresponding NSArray, used as data source for my table.
Now what I want is to reload datas using this functions and refresh my tableView once this 3 functions are done, but without disturbing the user.
I'm not used with asynchronous request, blocks, threads etc... and I'm looking for tips.
Actually, here is what I do :
-(void)viewDidLoad
{
//some settings
[NSTimer scheduledTimerWithTimeInterval:15.0 target:self selector:#selector(reloadDatas) userInfo:nil repeats:YES];
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
[self reloadDatas];
});
}
-(void)reloadDatas
{
dispatch_queue_t concurrentQueue = dispatch_get_main_queue();
dispatch_async(concurrentQueue, ^{
[self f1];
[self f2];
[self f3];
[myDisplayedTable reloadData];
});
}
-(void)f1
{
//load datas with a url request and update array1
}
-(void)f2
{
//load datas with a url request and update array2
}
-(void)f3
{
//load datas with a url request and update array3
}
But here, my tableView is "frozen" until it is refreshed.
I don't care about the order of execution of f1 f2 and f3, but I need to wait for this 3 functions to be done before refresh my tableView.
Thanks for your help.
EDIT
Thanks for all your answers.
Here is the working solution :
As mros suggets, I removed the dispatch queue from the viewDidLoad, and replace in reloadDatas:
dispatch_queue_t concurrentQueue = dispatch_get_main_queue();
with
dispatch_queue_t mainThreadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
And finally, I reload my table in a main thread
dispatch_async(dispatch_get_main_queue(), ^{ [myDisplayedTable reloadData]; });
So your "background thread" is actually your main thread. You have to use dispatch_get_global_queue and specify a priority to actually get a different thread. Also, the dispatch async in viewDidLoad is useless as all view controller lifecycle methods are called in the main thread. I would recommend doing something as follows in your f1, f2 and f3 methods:
Start by launching an asynchronous url request, then in the completion block, update arrayX and reload a particular section of your tableview. This way all three requests can happen simultaneously and the table just updates the necessary data when each one finishes. Alternatively, if you only want to reload once, just replace the concurrentQueue variable you have with a background thread and then perform [tableView reloadData] on the main thread.
The previous answers are absolutely right. However your implementation of reloadDatas & viewDidLoad is a bit problematic.
Just to clarify:
You want to complete the time consuming data loading stuff in a background thread, then update the UI/Cells when your data is ready on the main thread.
Like so:
-(void)viewDidLoad
{
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.my.backgroundQueue", NULL);
dispatch_async(concurrentQueue, ^{
[self reloadDatas];
});
}
-(void)reloadDatas
{
// Expensive operations i.e pull data from server and add it to NSArray or NSDictionary
[self f1];
[self f2];
[self f3];
// Operation done - now let's update our table cells on the main thread
dispatch_queue_t mainThreadQueue = dispatch_get_main_queue();
dispatch_async(mainThreadQueue, ^{
[myDisplayedTable reloadData]; // Update table UI
});
}
One other thing. Pulling data from a server and updating table cells is pretty common.
No need for queues or timers here.
Here's an alternative structure.
Say you're pulling mp3's from your server :
Your model class is : Music.h/m
Your Model manager is : MusicManager.h/m (Singleton) - it will contain an array of music objects - that singleton is basically your datasource;
and finally your UItableViewController : MusicTableVC.h/m
In MusicManager.h/m : You have an NSMutableArray which will be loaded with Music.h objects that you've pull from the server. You can do that as soon as you app loads without even waiting for the TableViewController.
Inside MusicManager you have a few helper methods to add or remove items from the mutableArray and provide the count and of course your networking methods.
Finally : Post a notification in your network code. Your UITableViewController should listen/observe that notification and "reload" accordingly.
[[NSNotificationCenter defaultCenter] postNotificationName:#"NewMusicAdded" object:nil];
You query data from your server, parse the data into Music objects add them to your NSMutable array and post a notification to let the table update itself.
Pretty standard recipe.
In reloadDatas method you should change this line:
dispatch_queue_t concurrentQueue = dispatch_get_main_queue();
To:
dispatch_queue_t concurrentQueue = dispatch_queue_create("some queue", NULL);
But when you call [myDisplayedTable reloadData], you need to call this operation in the main queue.
dispatch_async(dispatch_get_main_queue(), ^{ [myDisplayedTable reloadData]; });
I want to synchronize some data with a web service. For each item I have to make a asynchronous call.
I want to have a completion block witch is called, when each item was synchronized. For each item I am able to perform a completion block. Now, I don't know a good way how to do it.
This is the interface:
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
for (int i = 0, n = [items count]; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
// What do do here?
}];
}
// And/or here?
}
-(void) synchronizeItemOnComplete:(CompleteBlock) block {
// do something
block();
}
How can I wait for the synchronization and then perform the block?
I tried something like this:
NSArray* items = // get items
__block int countOfItemsUntilDone = [items count];
for (int i = 0, n = countOfItemsUntilDone; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
countOfItemsUntilDone--;
}];
}
dispatch_queue_t queue = dispatch_queue_create("wait for syncing", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
while (countOfItemsUntilDone > 0) {
usleep(1000); // wait a little bit
}
block();
});
dispatch_release(queue);
But I think this is a quite bad way. Any ideas?
Instead of spinning in a loop waiting for the counter to equal zero, check the counter value each time you decrement it, then fire an event when it reaches zero.
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
__block NSUInteger remaining = [items count];
for (ItemClass* item in items) {
[self synchronizeItemImage:item onComplete:^{
--remaining;
if (remaining == 0) {
block();
}
}];
}
}
To explain why it feels wrong, there are two things you're doing here that you should do either never or rarely:
Using background queues. This is difficult and bug-prone. Don't do it without reading up a lot about writing concurrent code. You also only really need to do this if an operation blocks for a substantial amount of time (eg., to read a file from disk, or perform an intensive calculation). Don't assume you need to do it unless you have a good reason (eg., a measurable performance problem).
Spinning in a loop, checking a variable for changes and calling sleep. You should never do this.
Also, if you're looping over the elements in an array, the for ... in syntax is much nicer (and potentially more efficient) calling objectAtIndex: on each index.
Never check or decrement shared memory in different threads like this, it can cause races. Use a dispatch group to do what you're doing.
dispatch_queue_t myBGQueue;
dispatch_group_t itemsGroup = dispatch_group_create();
for (ItemClass *item in items) {
dispatch_group_async(itemsGroup, myBGQueue, ^{
[self synchronizeItemImage:item];
});
}
/* execution will sleep here until all the blocks added in the `for` complete */
dispatch_group_wait(itemsGroup, DISPATCH_TIME_FOREVER);
dispatch_release(itemsGroup);
You can use these to use synchronously.
GCD and this
performSelector:waitUntilDone:YES