NSOperationQueue serial FIFO queue

末鹿安然 提交于 2019-11-26 18:18:42

问题


Is it possible to use an NSoperationQueue object as a serial FIFO queue by setting its maxConcurrentOperationCount to 1?

I note that the docs state...

For a queue whose maximum number of concurrent operations is set to 1, this equates to a serial queue. However, you should never rely on the serial execution of operation objects.

Does this mean that FIFO execution is not guaranteed?


回答1:


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.




回答2:


The queue is not FIFO as mentioned by the documentation. You can make it strictly FIFO if you ensure that any new operation depends on the last operation added in the queue and that it can only run a single operation at a time. Omar solution is correct, but more generally, you can do the following:

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

NSOperation* someOperation = [ NSBlockOperation blockOperationWithBlock:^(void) { NSLog(@"Done.");} ];

if ( queue.operations.count != 0 )
    [ someOperation addDependency: queue.operations.lastObject ];

This works because queue.operations is an array: whatever you add is not reordered (it is not a NSSet for instance). You can also simply add a category to your NSOperationQueue:

@interface NSOperationQueue (FIFOQueue)
- (void) addOperationAfterLast:(NSOperation *)op;
@end

@implementation NSOperationQueue (FIFOQueue)

- (void) addOperationAfterLast:(NSOperation *)op
{
    if ( self.maxConcurrentOperationCount != 1)
        self.maxConcurrentOperationCount = 1;

    NSOperation* lastOp = self.operations.lastObject;
    if ( lastOp != nil )
        [ op addDependency: lastOp ];

    [ self addOperation:op];
}

@end

and use [queue addOperationAfterLast:myOperation]. queuePriority has nothing to do with FIFO, it is related to job scheduling.

Edit: following a comment below, suspending the queue if checking for the count is also not sufficient. I believe this form is fine (upon testing, this does not create a race condition and does not crash).

Some info: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperationQueue_class/#//apple_ref/occ/instp/NSOperationQueue/suspended




回答3:


To make a simple FIFO using nsInvocationopration You will need to set one operation to be depended on the other Using addDependency: method

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *oper1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"1"];

NSInvocationOperation *oper2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"2"];
NSInvocationOperation *oper3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSth:) object:@"3"];

[oper2 addDependency:oper1];
[oper3 addDependency:oper2];
//oper3 depends on oper2 wich depends on oper1
//order of execution will ber oper1->oper2->oper3

//Changing the oreder will not change the result
[queue addOperation:oper2];
[queue addOperation:oper3];
[queue addOperation:oper1];


- (void) doSth:(NSString*)str
{
    NSLog(str); //log will be 1 2 3
    //When you remove the addDependency calls, the logging result that i got where
    //different between consecutive runs i got the following
    //NSLog(str); //log will be 2 1 3
    //NSLog(str); //log will be 3 1 2
}

Note: if you are using NSInvocationOperation then setting the maxConcurrentOperationCount to 1 would most likely do the trick to you, since isReady will not be editable by you

But maxConcurrentOperationCount = 1 would not be a good solution if you are planing to create your own subclasses of NSOperation

Since in NSOperation derivatives you could override the isReady function and return no, (imagine some operation that would need to wait some data from a server to in order to function properly) in these cases you would return isReady no until you are really ready In these cases you will need to add dependencies between operations inside the queue

From apple docs this equates to a serial queue. However, you should never rely on the serial execution of operation objects. Changes in the readiness of an operation can change the resulting execution order



来源:https://stackoverflow.com/questions/10948804/nsoperationqueue-serial-fifo-queue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!