NSOperationQueue串行FIFO队列

时间:2012-06-08 12:24:06

标签: ios objective-c nsoperationqueue fifo serial-processing

是否可以通过将NSoperationQueue设置为1来将maxConcurrentOperationCount对象用作串行FIFO队列?

我注意到docs州......

  

对于最大并发操作数设置为1的队列,这相当于一个串行队列。但是,您永远不应该依赖于操作对象的串行执行。

这是否意味着无法保证FIFO执行?

3 个答案:

答案 0 :(得分:23)

在大多数情况下,它将是FIFO。但是,您可以在NSOperations之间设置依赖关系,以便提前提交的操作将允许其他操作在队列中传递它,直到其依赖关系得到满足。

此依赖关系管理是文档表明无法保证FIFO-ness的原因。但是,如果你没有使用依赖关系,你可以依赖它。

<强>更新: NSOperation还具有queuePriority属性,该属性还可以使操作以非FIFO顺序执行。没有挂起依赖项的最高优先级操作将始终首先执行。

NSOperation子类也可能会覆盖-isReady,这可能会导致它回到队列中。

因此,保证队列上的执行是 serial ,因为在此队列中一次只能运行一个操作。但Apple无法保证FIFO;这取决于你对所投入的操作做了什么。

答案 1 :(得分:12)

队列不是文档中提到的F​​IFO。如果确保任何新操作取决于队列中添加的最后一个操作,并且它一次只能运行一个操作,则可以将其严格设置为FIFO。 Omar解决方案是正确的,但更一般地说,您可以执行以下操作:

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 ];

这是因为queue.operations是一个数组:你添加的任何内容都没有重新排序(例如它不是NSSet)。您也可以简单地在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

并使用[queue addOperationAfterLast:myOperation]。 queuePriority与FIFO无关,它与作业调度有关。

编辑:在下面的评论之后,如果检查计数,则暂停队列也是不够的。我相信这种形式很好(经过测试,这不会造成竞争条件,也不会崩溃)。

一些信息:https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperationQueue_class/#//apple_ref/occ/instp/NSOperationQueue/suspended

答案 2 :(得分:-3)

使用nsInvocationopration创建一个简单的FIFO 您需要将一个操作设置为依赖另一个操作 使用addDependency:方法

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
}

注意:如果您使用NSInvocationOperation,那么将maxConcurrentOperationCount设置为1很可能会对您有所帮助,因为isReady将无法由您编辑

但如果您计划创建自己的maxConcurrentOperationCount

子类,NSOperation = 1将不是一个好的解决方案

因为在NSOperation派生词中你可以覆盖isReady函数并返回no,(想象一些需要等待服务器中某些数据才能正常运行的操作)在这些情况下你会返回{{ 1}}直到你真的准备好了 在这些情况下,您需要在队列中的isReady no之间添加dependencies

从apple docs 这相当于一个串行队列。但是,您永远不应该依赖于操作对象的串行执行。操作准备就绪的更改可能会更改生成的执行顺序