I'm creating an application that fetches a set of results from a database - I use MBProgressHUD to show the progress of the query with an animation. The method I use calls the animation while executing a method in another thread and once it's done, it hides the animation. My question is, after calling:
[HUD showWhileExecuting:@selector(getResults) onTarget:self withObject:nil animated:YES];
I would like to, if there are no results, display an alert stating this, and if there are, load the next view. So far, I have this code:
[HUD showWhileExecuting:@selector(getResults) onTarget:self withObject:nil animated:YES];
if(self.thereAreEvents) {
[self performSegueWithIdentifier:@"searchResults" sender:self];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No results" message:@"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
self.thereAreEvents
gets set at the end of the getResults
method. However, since that method gets called in another thread this line of execution continues and shows the alert, even though there are events in the database.
So, from here, I have two questions: What is the easiest way to implement a wait-signal mechanism in iOS and what is the most efficient way to implement this sort of mechanism in iOS?
Thanks!
In between, we have also put the main thread to sleep by using TimeUnit. sleep() method. So the main thread can wait for some time and in the meantime, T1 will resume and complete its execution.
The statement “Thread. currentThread(). join()”, will tell Main thread to wait for this thread(i.e. wait for itself) to die.
Multithreading is an implementation handled by the host operating system to allow the creation and usage of n amount of threads. Its main purpose is to provide simultaneous execution of two or more parts of a program to utilize all available CPU time.
You can use a busy wait loop for a quick and dirty solution:
__block BOOL finished = NO;
dispatch_async(/* global queue */, ^{
// …
finished = YES;
});
while (!finished) /* waiting */;
In “real” code it’s better to use a semaphore:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(/* global queue */, ^{
// …
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(sempahore);
This is better than the busy loop because the blocked thread does not eat CPU time.
The best solution is to keep from blocking and redesign your code to work asynchronously. In your case, you should display a spinner and start downloading data. When the data is finished downloading, you should receive an asynchronous callback (either through a block or a target/action callback) and display the results or show the error alert. Blocking with a busy loop or a semaphore is a poor man’s solution in this case.
You could also consider an NSConditionLock
.
So it'd be something like this on thread 1:
[conditionLock lockWhenCondition:kConditionOkayToProceed];
[conditionLock unlockWithCondition:kConditionGettingResults];
[HUD show...]
[conditionLock lockWhenCondition:kConditionResultsFetched];
[conditionLock unlockWithCondition:kConditionOkayToProceed];
And in the HUD:
- (void)show...
{
[conditionLock lockWhenCondition:kConditionGettingResults];
// stuff here
[conditionLock unlockWithCondition:kConditionResultsFetched];
}
Though a much better solution would be to pass a block or a target/selector to the HUD that it is to perform when results are fetched.
EDIT: so you'd end up with code like:
[HUD showWhileExecuting:@selector(getResults)
onTarget:self
withObject:nil
animated:YES
performWhenFinished:
^{
if(self.thereAreEvents) {
[self performSegueWithIdentifier:@"searchResults" sender:self];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No results" message:@"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}];
And in the HUD:
- (void)showWhile... performWhenFinished:(dispatch_block_t)block
{
// all the other stuff you were going to do here, then
// eventually...
// if no guarantees, maybe just:
block();
// otherwise, if promised to dispatch to the main queue:
dispatch_async(dispatch_get_main_queue(), block);
}
With HUD having the extra intelligence to take a dispatch_block_t
as a final argument and to call it when results are in (whether guaranteeing dispatch back to the main thread or otherwise).
This is your way: Concurrency Programming Guide
Also: Synchronization
More sharp: Using Locks. I think the last one could give the best help.
Another simple approach, it's bad but works
NSAssert(![NSThread isMainThread], @"Do not run on MAIN thread");
while (yourCondition) { [NSThread sleepForTimeInterval:0.2]; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With