Nsoperationqueue Serial Fifo Queue

NSOperationQueue serial FIFO queue

In most cases, it will be FIFO. However, you can set up dependencies between NSOperations such that an operation submitted early will let other operations pass it by in the queue until its dependencies are satisfied.

This dependency management is why the docs indicate that FIFO-ness cannot be guaranteed. If you're not using dependencies, though, you should be fine to rely on it.

Update:
NSOperation also has a queuePriority property, which can also cause operations to execute in non-FIFO order. The highest-priority operation with no pending dependencies will always execute first.

An NSOperation subclass might also override -isReady, which could cause it to move back in the queue.

So execution on your queue is guaranteed to be serial, in that no more than one operation will run at a time in this queue. But Apple can't guarantee FIFO; that depends on what you're doing with the operations you put in.

Is [NSOperationQueue mainQueue] a serial queue?

As per Apple Documentation [NSOperationQueue mainQueue] performs operation in serial manner:-

The [NSOperationQueue mainQueue] returned queue executes one operation at a time on the app’s main thread. The execution of operations on the main thread is interleaved with the other tasks that must execute on the main thread, such as the servicing of events and the updating of an app’s user interface. The queue executes those operations in the run loop common modes, as represented by the NSRunLoopCommonModes constant.

Now if you want to execute the operation in concurrent or parallel manner. Refer the below sample:-

NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSOperation * nwOperation = [[yourOper alloc] init];
[queue addOperation:nwOperation];

So if you add multiple operations to a queue, they all execute in parallel on background threads, allowing your main thread to deal with the user interface. The queue will intelligently schedule the number of parallel operations based on the number of CPU cores your users have, thus effectively taking advantage of your users’ hardware.

EDIT:-

For more information about concurrent operations refer this Dave Dribin's blog.

NSOperationQueue: Trouble understanding the order

If there are explicit dependencies between the operations, then use addDependency:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=1;

NSOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
[someObject someSelector];
}];

NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
[someObject anotherSelector];
}];

[operation2 addDependency:operation1];

[queue addOperation:operation1];
[queue addOperation:operation2];

If your operations are doing asynchronous activity, then you should define a custom operation, and only call completeOperation (which will post the isFinished message) when the asynchronous task is done).

//  SomeOperation.h

#import <Foundation/Foundation.h>

@interface SomeOperation : NSOperation

@end

and

//  SomeOperation.m

#import "SomeOperation.h"

@interface SomeOperation ()
@property (nonatomic, readwrite, getter = isFinished) BOOL finished;
@property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
@end

@implementation SomeOperation

@synthesize finished = _finished;
@synthesize executing = _executing;

#pragma Configure basic operation

- (id)init
{
self = [super init];
if (self) {
_finished = NO;
_executing = NO;
}
return self;
}

- (void)start
{
if ([self isCancelled]) {
self.finished = YES;
return;
}

self.executing = YES;

[self main];
}

- (void)completeOperation
{
self.executing = NO;
self.finished = YES;
}

- (void)main
{
// start some asynchronous operation

// when it's done, call `completeOperation`
}

#pragma mark - Standard NSOperation methods

- (BOOL)isConcurrent
{
return YES;
}

- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}

- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}

@end

Thus, with the following code, it won't start operation2 until the asynchronous task initiated in main in SomeOperation object, operation1, calls its completeOperation method.

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=1;

NSOperation *operation1 = [[SomeOperation alloc] init];

NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
[someObject anotherSelector];
}];

[operation2 addDependency:operation1];

[queue addOperation:operation1];
[queue addOperation:operation2];

How to make the NSOperationQueue serial?

If you want a serial queue, you are right setting maxConcurrentOperation to one. You can also use [NSOperationQueue mainQueue] instead of creating a new queue, and so queue operations on the main thread. But that is only useful if very short operations are added and so the user interface is not blocked. And on the other hand you have not to worry about threads n synch.

You can add operations to any queue with addOperations:waitUntilFinished:YES or sending the message waitUntilAllOperationsAreFinished every time you add an operation. That way you are serializing operations instead of defining the queue as serial.

NSOperationQueue addOperations waitUntilFinished

A couple of issues:

  1. You are examining behavior of the completion block handlers. As the completionBlock documentation says:

    The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context.

    The queue will manage the operations themselves, but not their completion blocks (short of making sure that the the operation finishes before its completionBlock is started). So, bottom line, do not make any assumptions about (a) when completion blocks are run, (b) the relation of one operation's completionBlock to other operations or their completionBlock blocks, etc., nor (c) which thread they are performed on.

  2. Operations are generally executed in the order in which they were added to the queue. If you add an array of operations, though, the documentation makes no formal assurances that they are enqueued in the order they appear in that array. You might, therefore, want to add the operations one at a time.

  3. Having said that, the documentation goes on to warn us:

    An operation queue executes its queued operation objects based on their priority and readiness. If all of the queued operation objects have the same priority and are ready to execute when they are put in the queue—that is, their isReady method returns YES—they are executed in the order in which they were submitted to the queue. However, you should never rely on queue semantics to ensure a specific execution order of operation objects. Changes in the readiness of an operation can change the resulting execution order. If you need operations to execute in a specific order, use operation-level dependencies as defined by the NSOperation class.

    To establish explicit dependencies, you might do something like:

    let oldify = NSBlockOperation() {
    NSLog("oldify")
    }
    oldify.completionBlock = {
    NSLog("oldify completion")
    }

    let appendify = NSBlockOperation() {
    NSLog("appendify")
    }
    appendify.completionBlock = {
    NSLog("appendify completion")
    }

    appendify.addDependency(oldify)

    let nettoyify = NSBlockOperation() {
    NSLog("nettoyify")
    }
    nettoyify.completionBlock = {
    NSLog("nettoyify completion")
    }

    nettoyify.addDependency(appendify)

    let queue = NSOperationQueue()
    queue.addOperations([oldify, appendify, nettoyify], waitUntilFinished: false)
  4. BTW, as you'll see above, you should not add operations to the main queue in conjunction with the waitUntilFinished. Feel free to add them to a different queue, but don't dispatch from a serial queue, back to itself, with the waitUntilFinished option.



Related Topics



Leave a reply



Submit