GCD, Threads, Program Flow and UI Updating GCD, Threads, Program Flow and UI Updating multithreading multithreading

GCD, Threads, Program Flow and UI Updating


GCD and performSelectorInBackground samples below. But first, let's look at your code.

You cannot wait where you want to in the code above.Here's the code you had. Where you say wait in the comment is incorrect. See where I added NO.

- (void) solvePuzzle:(id)sender{    solveButton.enabled = NO;    solveButton.title = @"Working . . . .";    // I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state.    [self performSelectorInBackground:@selector(createTreeFromNode:) withObject:rootNode];    // NO - do not wait or enable here.    // Need to wait here until createTreeFromNode is finished.    solveButton.enabled=YES;}

A UI message loop is running on the main thread which keeps the UI running. solvePuzzle is getting called on the main thread so you can't wait - it will block the UI. It also can't set the button back to enabled - the work hasn't been done yet.

It is the worker function's job on the background thread to do the work and then when it's done to then update the UI. But you cannot update the UI from a background thread. If you're not using blocks and using performSelectInBackground, then when you're done, call performSelectorOnMainThread which calls a selector to update your UI.

performSelectorInBackground Sample:

In this snippet, I have a button which invokes the long running work, a status label, and I added a slider to show I can move the slider while the bg work is done.

// on click of button- (IBAction)doWork:(id)sender{    [[self feedbackLabel] setText:@"Working ..."];    [[self doWorkButton] setEnabled:NO];    [self performSelectorInBackground:@selector(performLongRunningWork:) withObject:nil];}- (void)performLongRunningWork:(id)obj{    // simulate 5 seconds of work    // I added a slider to the form - I can slide it back and forth during the 5 sec.    sleep(5);    [self performSelectorOnMainThread:@selector(workDone:) withObject:nil waitUntilDone:YES];}- (void)workDone:(id)obj{    [[self feedbackLabel] setText:@"Done ..."];    [[self doWorkButton] setEnabled:YES];}

GCD Sample:

// on click of button- (IBAction)doWork:(id)sender{    [[self feedbackLabel] setText:@"Working ..."];    [[self doWorkButton] setEnabled:NO];    // async queue for bg work    // main queue for updating ui on main thread    dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);    dispatch_queue_t main = dispatch_get_main_queue();    //  do the long running work in bg async queue    // within that, call to update UI on main thread.    dispatch_async(queue,                    ^{                        [self performLongRunningWork];                        dispatch_async(main, ^{ [self workDone]; });                   });    }- (void)performLongRunningWork{    // simulate 5 seconds of work    // I added a slider to the form - I can slide it back and forth during the 5 sec.    sleep(5);}- (void)workDone{    [[self feedbackLabel] setText:@"Done ..."];    [[self doWorkButton] setEnabled:YES];}


  dispatch_queue_t backgroundQueue;  backgroundQueue = dispatch_queue_create("com.images.bgqueue", NULL);            - (void)process {        dispatch_async(backgroundQueue, ^(void){    //background task        [self processHtml];    dispatch_async(main, ^{ // UI updates in main queue   [self workDone];     });    });      });     }


By and large, any work to be submitted to a background queue needs to follow this pattern of code:

dispatch_queue_t queue = dispatch_queue_create("com.myappname", 0);__weak MyClass  *weakSelf = self;  //must be weak to avoid retain cycle//Assign async workdispatch_async(queue, ^{    [weakSelf doWork];    dispatch_async(dispatch_get_main_queue(),    ^{       [weakSelf workDone];     }); });  queue = nil;  //Using ARC, we nil out. Block always retains the queue.

Never Forget:

1 - queue variable above is a reference counted object, because it is a private queue, not a global one. So it is retained by the block which is executing inside that queue. Until this task is complete, it is not released.

2 - Every queue got its own stack which will be allocated / deallocated as part of recursive operation. You only need to worry about class member variables which are reference counted (strong, retain etc.) which are accessed as part of doWork above.

3 - While accessing those reference counted vars inside background queue operation, you need to make them thread-safe, depending on use cases in your app. Examples include writes to objects such as strings, arrays etc. Those writes should be encapsulated inside @synchronized keyword to ensure thread-safe access.

@synchronized ensures no another thread can get access to the resource it protects, during the time the block it encapsulates gets executed.

@synchronized(myMutableArray){    //operation}

In the above code block, no alterations are allowed to myMutableArray inside the @synchronized block by any other thread.