NSOperationQueue maxConcurrentOperationCount 1 Serial Queue Problem

Around the web and even in Apple’s WWDC 2015 Advanced NSOperations talk they say:

“If we set the max concurrent operation count of an NSOperationQueue to be 1, then we essentially make our NSOperationQueue into a serial operation queue.”

Sometimes developers also refer to this configuration as a first in first out (FIFO) queue. It works ok in very simple cases but by ignoring operation dependencies things can go bad fast. To illustrate what I mean, see this sample code:

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1");
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2");
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op3");
    }];
    
    NSBlockOperation *waitOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op4");
    }];
    
    // uncomment to fix problem
    // [op2 addDependency:op1];
    // [op3 addDependency:op2];

    [op2 addDependency: waitOp];
    
    [queue addOperations:@[op1, op2, op3] waitUntilFinished:NO];

The output from this code is:

2017-01-07 22:11:43.322770 OpTest[24703:35783606] op1
2017-01-07 22:11:43.322846 OpTest[24703:35783564] op3

What has happened is since op2 has a dependency on waitOp (which has been made unable to be started), op3 has skipped the queue and jumped ahead of op2. If it was a serial queue then you might expect only “op1” to be output and the queue waiting on waitOp to finish to then start op2.

By uncommenting the 2 addDependency lines we then get the expected behaviour:

2017-01-07 22:27:23.876388 OpTest[24909:35842006] op1

The funny thing is that with the dependencies set up correctly we can now remove the line that sets the maxConcurrentOperationCount and the behaviour stays correct. The problem is that usually developers want to retry their operations which does require understanding dependencies in which case I believe it would have been better to learn them from the outset rather than use this trick. Also if they do then they will be one step closer to learning the real power which is cross-queue dependencies.

Leave a Reply

Your email address will not be published. Required fields are marked *