Premature exit from dispatch_asycn , Grand Central Dispatch - iphone

Lets say i am running some code in dispatch async. .. is there a way to terminate the thread it creates before it completes? like when the user clicks cancel
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//start doing something here.. break bofore it finishes?
dispatch_async(dispatch_get_main_queue(), ^{
//main thread stuff..
});
});

David is right. GCD has no built-in method of cancellation. It is up to the client (you).

Related

Threading issue in iOS app

I am somewhat new to iOS development and am having an issue with threading. I am calling a web service that returns json data and the code to perform this action works as expected. For testing, i would like to be able to click a button, retrieve the data and populate a textview control with formatted results. Here is my code excerpted from a button click event handler:
dispatch_queue_t que = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(que, ^{
thisRiverGauge = [[RiverGauge alloc] initWithStationInfo:gauge forHistoryInHours:5 inThisFormat:#"json"];
[txtResults setText:rval];
});
When trying to update the textview (txtResults) from within the thread, I get a runtime error. When I place the update to the textview outside of the thread, obviously it won't update because the thread takes longer to complete than the execution of the event handler. What might be a solution to this?
Thx!
You should perform GUI related tasks on the main thread, add the main queue/thread block around the code where you are updating the value for textview.
ispatch_queue_t que = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(que, ^{
thisRiverGauge = [[RiverGauge alloc] initWithStationInfo:gauge forHistoryInHours:5 inThisFormat:#"json"];
dispatch_async(dispatch_get_main_queue(), ^{
[txtResults setText:rval];
});
});

Objective-c/iOS: setting status text in async function is slow

In my app I'm doing some communication with a remote server and as this might be slow I thought it would be a good idea to run that code asynchronously. I have my communication code in a block that I pass to dispatch_async. This code does the communication and when it's done it sets the text of a label. This last part is the problem. The text is set, but it occurs after a delay of a few seconds. This is my code.
- (void)doNetworkingTask {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Slow network task goes here.
// Slow network task done, notify the user.
[self.myLabel setText:#"task done."];
NSLog(#"task done.");
});
}
What happens here is that my network task completes, the NSLog-text is logged and after a couple of seconds, the text of the label is updated. My question is 1) why does the label text not update instantly? and 2) what is a proper way of doing what I want to do? (do the slow network task without blocking anything else, update the user through a text label once I'm done.)
UI updates must be on the main thread. Update your code to something like this:
- (void)doNetworkingTask {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Slow network task goes here.
// Slow network task done, notify the user.
dispatch_async(dispatch_get_main_queue(), ^{
[self.myLabel setText:#"task done."];
});
NSLog(#"task done.");
});
}

Multiple GCD Dispatches on main thread

I'm trying to speed up the boot of my app, and one of the ideas i had for that was to use asynchronous dispatch queues. I have 2 tasks that can be run next to each other at startup (quite big tasks actually). However, both of them have a significant part that runs on the main thread (UI code mainly).
dispatch_async(dispatch_get_main_queue, ^{
[self doTask1];
});
dispatch_async(dispatch_get_main_queue, ^{
[self doTask2];
//Will task 2 take turns with task 1, or will task 2 start after 1 is finished?
});
My question is this: If i call 2 dispatch_async's at boot like in this example, will they take turns in executing, or will the complete first block execute first, then the 2nd block?
the main queue is a serial queue. blocks added to serial queues are executed in the order they are added and only one at a time (serially). in your example, task2 will not start until task1 has finished.
if you want them to run concurrently you'll need to dispatch them to one of the global concurrent queues.
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(q, ^{/* this work may finish before other work added to this queue later */});
One will be executed after the other, but they will execute concurrently, meaning you can execute task2 before task1 has finished.
Check the alternative:
dispatch_async(dispatch_get_main_queue, ^{
[self doTask1];
dispatch_async(dispatch_get_main_queue, ^{
[self doTask2];
//Now task2 will execute after task1
});
});

stop and restart a method

I have to stop and restart a simple method that takes from 1 to 2 seconds to execute. How can this be accomplished? I have already tried [NSObject cancelPreviousPerformRequestsWithTarget:self] but it works only with performselector after delay. I have also tried to create a new thread but it doesn't seem to work...
This is my method:
-(IBAction)MyMethod
{
NSLog(#"start");
//Here is the code that takes time to execute. It regards UI intervention,graphic calculation, x and y position etc.
NSLog(#"end");
}
I want this effect: one click on the linked UIButton and the method start (so print start log and end log). If I click the linked UIButton before the NSLog is printed, the method must stop. Is this possible?
You'll want to use a background task. I would suggest subclassing and using a NSOperation and checking for isCancelled within the body of main. See Apple's documentation on using NSOperation and NSOperationQueue.
Well, to do this with threads what I usually do is dividing things into 3 sections:
start.
processing.
finish.
And it looks like this:
-(void)start:(id)sender{
//prepare everything and anything
[NSThread detachNewThreadSelector:#selector(processing:) toTarget:self withObject:nil];
}
-(void)processing:(id)sender{
//Perform all your calculations, you can't modify UI elements here
[self performSelectorOnMainThread:#selector(finish:) withObject:nil waitUntilDone:NO];
}
-(void)finish:(id)sender{
//Wrap everything up and do any modifications to the UI
}
Now, to cancel this you could add maybe use:
Cancels perform requests previously registered with
performSelector:withObject:afterDelay:.
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument

Grand Central Dispatch problems with dispatch_release

I have a problem with a GCD solution I've made. I have a thread that runs in the background, it updates the application in a given interval. For this I use a timer.
However if the user wants to change this interval, I call
dispatch_source_cancel(timer);
Which is defined as
dispatch_source_set_cancel_handler(timer, ^{
dispatch_release(timer);
});
And then restart the the thread. When the interval is changed a second time the app crashes. Even though I do recreate the timer with a new interval.
I could avoid releasing the timer, but then I'll have memory leeks.
Any advice, what to do?
EDIT:
Timer is created like this
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0, autoRefreshQueue);
if(!timer) {
return;
}
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, refreshRate * NSEC_PER_SEC), refreshRate * NSEC_PER_SEC, refreshRate * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
//do work
});
I don't think this is the answer. dispatch_source_cancel doesn't cancel immediately, synchronously.
man dispatch_source_cancel
The dispatch_source_cancel() function asynchronously cancels the dispatch source, preventing any further invocation of its event handler block. Cancellation does not interrupt a currently executing handler block (non-preemptive).
Thus, restarting the thread might invoke the blocks concurrently if autoRefreshQueue is Global Queue.
How did you restart the thread?
EDITED:
However there are no mentions of calling dispatch_source_set_timer twice (or more) for the same Dispatch Source in the references or the manuals, dispatch_source_set_timer in libdispatch/src/source.c seems ok for it. At least, as far as my test, there are no problem.
Thus, just call dispatch_source_set_timer for a new interval.
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, refreshRate * NSEC_PER_SEC), refreshRate * NSEC_PER_SEC, refreshRate * NSEC_PER_SEC);