subharb
subharb

Reputation: 3472

how do dispatch queues work

Im a bit confused here, Im using queues and I got to a point where Im a bit lost.

I have a method named getPeople who has to fetch pictures of users from the server. In order not to block the app I used this:

 -(IBAction)seeMorePeople{
        dispatch_queue_t getPeopleQueue = dispatch_queue_create("Pinta Ocupantes", NULL);
        dispatch_async(getPeopleQueue, ^{
           [self getPeople];
        });
       dispatch_release(getPeopleQueue);
 }

The previous code is executed everytime the user taps a button. Something like "Give me pics from this album" and then another tap "Now I want people's pictures from that other album", diferent pics and different amount of pictures. If the user taps the buttons quite fast, the first queue wont finish fetching the data when the second one is already starting. With in getPeople I store the data in an NSMutableArray, so when the 2 queues are executing at the same time both are writing on the same Array and the app crashes due to out of bounds exception. The way getPeople goes through the data is something like this:

-(void)getPeople:(NSDictionary *)peopleDictionary{
    //I receive an NSDictionary and I go through it
    NSArray *keys = [peopleDictionary allKeys];
    int indexOfArray = 0;
    for(NSString *key in keys){
         //Complex operation that are not important
         [peopleInArray insertObjetAtIndex:indexOfArray];//People in array is a global variable
         indexOfArray++;
    }
}

What I can't figure out is how to get out of this, I thought of stopping the first queue when the second one comes in, but GCD doesnt have this option... any other way to get this done, hopefully without a major recoding, anyway right now Im out of ideas, so any clue will help.

Upvotes: 8

Views: 24694

Answers (3)

Kumar
Kumar

Reputation: 1

NSString *lastLoginTime =@" Your last login time";

    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate* date1 = [dateFormat lastLoginTime];
    NSDate* date2 = [dateFormat dateFromString:[NSString currentDateTime]];
    NSTimeInterval secondsSinceLastLogin = [date2 timeIntervalSinceDate:date1];
    //  NSLog(@"Seconds Since Last Login: %g", secondsSinceLastLogin);
    int hours = (int)secondsSinceLastLogin / 3600;

Upvotes: -1

Ken Thomases
Ken Thomases

Reputation: 90551

I would suggest that you avoid synchronizing with semaphores, if possible. The design of GCD is to avoid that. A background task should prepare data but not touch outside state. When the data is prepared, it should dispatch the updating of outside state to either a serial queue or, if the state is bound to the GUI, to the main queue.

So, something like this:

-(void)getPeople:(NSDictionary *)peopleDictionary{
    //I receive an NSDictionary and I go through it
    NSArray *keys = [peopleDictionary allKeys];
    for(NSString *key in keys){
         //Complex operation that are not important
         dispatch_async(dispatch_get_main_queue(), ^{
             [peopleInArray addObject:<whatever>];
         });
    }
}

If you rather want to replace the array, instead of having two threads adding to it in interleaved fashion, you'd accumulate the whole array in the background and dispatch setting the entirety of peopleInArray to the main queue.

If you want cancellation, you can implement it yourself with a flag, or you should maybe consider using NSOperation and NSOperationQueue instead of GCD. Those have a concept of cancellation built in, although your custom operations still need to check if they've been cancelled and stop working.

Upvotes: 12

Max
Max

Reputation: 989

You are right, there is no way to stop a queue which was dispatched. One thing you could do to make sure that only one queue is accessing getPeople at the same time is using semaphores (this might be even more interesting).

If you just want to avoid that the users clicks the button multiple times you could use a bool variable stillExecuting which is set to YES in your asynchronous dispatch and set to NO at the end of getPeople. Before creating getPeopleQueue you simply check if getPeople is still executing.

if(!stillExecuting) {
       dispatch_queue_t getPeopleQueue = dispatch_queue_create("Pinta Ocupantes", NULL);
        dispatch_async(getPeopleQueue, ^{
           [self getPeople];
        });
       dispatch_release(getPeopleQueue);
}

Upvotes: 2

Related Questions