I've created a test project in which I'm testing my assumptions about NSOperation
and NSOperationQueue
before using them in my main project.
My code is pretty simple, so I'm going to include all of it here. This is using a command line Foundation project with ARC turned on.
#import <Foundation/Foundation.h>
@interface Operation : NSOperation
@property (readwrite, strong) NSString *label;
- (id)initWithLabel: (NSString *)label;
@end
#import "Operation.h"
@implementation Operation
- (void)main
{
NSLog( @"Operation %@", _label);
}
- (id)initWithLabel: (NSString *)label
{
if (( self = [super init] )) {
_label = label;
}
return self;
}
@end
#import <Foundation/Foundation.h>
#import "Operation.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
id create = [[Operation alloc] initWithLabel: @"create"];
id update1 = [[Operation alloc] initWithLabel: @"update1"];
id update2 = [[Operation alloc] initWithLabel: @"update2"];
[update1 addDependency: create];
[update2 addDependency: create];
[queue addOperation: update1];
[queue addOperation: create];
[queue addOperation: update2];
[queue waitUntilAllOperationsAreFinished];
}
return 0;
}
My output looks like this:
2012-05-02 11:37:08.573 QueueTest[1574:1b03] Operation create
2012-05-02 11:37:08.584 QueueTest[1574:1903] Operation update2
2012-05-02 11:37:08.586 QueueTest[1574:1b03] Operation update1
Having written this and experimented with a few combinations, I found that when I reordered the queue setup like this:
[queue addOperation: update1];
[queue addOperation: create];
[queue addOperation: update2];
[update1 addDependency: create];
[update2 addDependency: create];
[queue waitUntilAllOperationsAreFinished];
I got the same output:
2012-05-02 11:38:23.965 QueueTest[1591:1b03] Operation create
2012-05-02 11:38:23.975 QueueTest[1591:1b03] Operation update1
2012-05-02 11:38:23.978 QueueTest[1591:1903] Operation update2
I should note that I have found in some runs that update2 is executed before update1, but that behaviour isn't surprising. Why should NSOperationQueue
be deterministic when I haven't asked it to be?
What I do find surprising is that somehow create is always executed before update1 and update2 even if everything is added to the queue before dependancies are added.
Obviously, this is a silly thing to do, but it's led me to wonder: Is the delay between when I add an operation to the queue and when it's executed documented, or in any way predictable? Exactly when does NSOperationQueue
start processing added operations?
Really, most importantly, exactly what is NSOperationQueue
waiting for and when will that wait bite me in some way I can't defend against?
Obviously, this is a silly thing to do, but it's led me to wonder: Is the delay between when I add an operation to the queue and when it's executed documented, or in any way predictable? Exactly when does NSOperationQueue start processing added operations?
After:
For an operation with no unsatisfied dependencies, this can mean immediately.
Further experimentation seems to show that NSOperationQueue starts running operations as soon as the current thread yields control to it, via [queue waitUntilAllOperationsAreFinished], [NSThread sleepForTimeInterval: 0.000001], or an interrupted thread.
This assumption is bogus, for two reasons:
addOperation:
even returns (though this extreme is unlikely simply
because of overhead). More importantly, the other operations may be able to run while the first one does.Both of these points, but especially the latter, also mean that the question of “order” is pretty much reduced to nonsense. Operations that you arrange in a dependency tree will be started in order down that tree, but otherwise there is no sequence at all; you should assume that operations with no dependency between them will run at the same time, not one before another.
Once an operation has been started, adding dependencies to it has no effect. Since that can happen immediately upon adding it to the queue, you must add dependencies to the operation before you add the operation to a queue if you want the dependencies to reliably have effect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With