16

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

我注意到文档状态...

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

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

4

3 回答 3

25

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

这种依赖管理是文档指出不能保证 FIFO 的原因。但是,如果您不使用依赖项,那么依赖它应该没问题。

更新: NSOperation 也有一个queuePriority属性,它也可以导致操作以非 FIFO 顺序执行。没有挂起依赖项的最高优先级操作将始终首先执行。

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

因此,您的队列上的执行保证是串行的,因为在此队列中一次不会运行一个以上的操作。但苹果不能保证先进先出;这取决于你对你投入的操作所做的事情。

于 2012-06-08T12:30:12.183 回答
14

队列不是文档中提到的 FIFO。如果您确保任何新操作都依赖于添加到队列中的最后一个操作并且一次只能运行一个操作,则可以使其严格 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

于 2015-09-21T18:01:58.523 回答
-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,如果您打算创建自己的子类, = 1 将不是一个好的解决方案NSOperation

由于在 NSOperation 派生类中,您可以覆盖该isReady函数并返回 no,(想象一些需要等待来自服务器的一些数据才能正常运行的操作)在这些情况下,您将返回isReady no,直到您真正准备好在这些情况下,您将需要dependenciesoperations队列内部添加

从苹果文档来看,这相当于一个串行队列。但是,您永远不应该依赖操作对象的串行执行。操作准备情况的变化可能会改变生成的执行顺序

于 2012-06-08T12:31:43.277 回答