Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCD serial queue does not seem to execute serially

I have a method that at times can be invoked throughout my code. Below is a very basic example, as the code processes images and files off of the iphone photo gallery and marks them already processed when done with the method.

@property (nonatomic, assign) dispatch_queue_t serialQueue;

....

-(void)processImages
{
    dispatch_async(self.serialQueue, ^{
        //block to process images
        NSLog(@"In processImages");

        ....

        NSLog(@"Done with processImages");
    });
}

I would think that each time this method is called I would get the below output... "In processImages" "Done with processImages" "In processImages" "Done with processImages" etc...

but I always get

"In processImages" "In processImages" "Done with processImages" "Done with processImages" etc...

I thought a serial queue would wait till the first block is done, then start. To me it seems it is starting the method, then it gets called again and starts up before the first call even finishes, creating duplicates of images that normally would not be processed due to the fact that if it really executed serially the method would know they were already processed. Maybe my understanding of serial queues is not concrete. Any input? Thank you.

EDIT:MORE Context below, this is what is going on in the block...Could this cause the issue???

@property (nonatomic, assign) dispatch_queue_t serialQueue;

....

-(void)processImages
{
    dispatch_async(self.serialQueue, ^{
        //library is a reference to ALAssetsLibrary object 

        [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop)
        {
            [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop)
            {
             ....
             //Process the photos here
            }];
        failureBlock:^(NSError *error) { NSLog(@"Error loading images from library");
        }];

    });
}

-(id)init
{
    self = [super init];
    if(self)
    {
        _serialQueue = dispatch_queue_create("com.image.queue",NULL);
    }
    return self;
}

this object is only created once, and as far as I can tell can never be created again based off my code...I will run tests to make sure though.

UPDATE 2: WHAT I THINK IS HAPPENING, please comment on this if you agree/disagree....

Obviously my main issue is that it seems this block of code is being executed concurrently, creating duplicate entries (importing the same photo twice) when it wouldn't normally do this if it was run serially. When a photo is processed a "dirty" bit is applied to it ensuring the next time the method is invoked it skips this image, but this is not happening and some images are processed twice. Could this be due to the fact I am enumerating the objects in a second queue using enumerategroupswithtypes: within that serialQueue?

  1. call processImages
  2. enumerateObjects
  3. immediately return from enumerateObjects since it is async itself
  4. end call to processImages

processImages is not really done though due to the fact that enumerategroups is probably still running but the queue might thing it is done since it reaches the end of the block before enumerategroups is finished working. This seems like a possibility to me?

like image 729
inks2002 Avatar asked Mar 25 '13 15:03

inks2002


Video Answer


1 Answers

Serial Queues ABSOLUTELY will perform serially. They are not guaranteed to perform on the same thread however.

Assuming you are using the same serial queue, the problems is that NSLog is NOT guaranteed to output results in the proper order when called near simultaneously from different threads.

here is an example:

  1. SQ runs on thread X, sends "In processImages"
  2. log prints "In proc"
  3. SQ on thread X, sends "Done with processImages"
  4. SQ runs on thread Y, sends "In processImages"
  5. log prints "essImages\n"

After 5., NSLog doesn't necessarily know which to print, 3. or 4.

If you absolutely need time ordered logging, You need a dedicated queue for logging. In practice, I've had no problems with just using the main queue:

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"whatever");
});

If all NSlog calls are the on the same queue, you shouldn't have this problem.

like image 186
amattn Avatar answered Sep 27 '22 22:09

amattn