2012-06-08 102 views
14

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

我注意到docs状态...

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

这是否意味着FIFO执行不能保证?

回答

23

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

这种依赖性管理是文档显示无法保证FIFO性能的原因。不过,如果你不使用依赖关系,那么依靠它应该没问题。

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

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

因此,您的队列上的执行保证为serial,因为此队列中一次只能运行一个操作。但是,苹果不能保证FIFO;这取决于你和你把作业做什么

+0

执行顺序还取决于操作的队列优先级。 – JeremyP

+0

非常真实。更新。 –

+0

因此,如果我的操作都具有相同的优先级,没有依赖关系,并且只依赖'isReady'的超类实现(我不覆盖),它应该导致一个FIFO队列? – Barjavel

-2

要使用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将不会对你

但编辑= 1就不会是一个很好的解决方案,如果你是刨去创建自己的NSOperation

子自的NSOperation衍生工具,你可以重写isReady功能和返回无,(想象一下一些操作,将需要等待一些数据一台服务器,以便在这些情况下正常工作),你将返回isReady no,直到你真的准备好 在这些情况下,您将需要operations之间添加dependencies队列

内从苹果公司的文档这相当于一个串行队列。但是,你不应该依赖于操作对象的串行执行。在操作的准备就绪的变化可改变所得执行顺序

+0

你能解释为什么依赖是必要的吗? –

+0

@BJHomer我已更新我的答案,包括一些日志记录 –

+0

您的队列在示例中没有像指定的OP那样的'maxConcurrentOperationCount = 1'。这套装置是否仍然会发生? –

12

由文档中提到的队列没有FIFO。如果您确保任何新操作取决于队列中添加的最后一个操作,并且它一次只能运行一个操作,则可以将其严格设置为FIFO。奥马尔的解决方案是正确的,但更普遍的,你可以做到以下几点:

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 

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

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

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

+0

这种类型的解决方案存在问题。我一直在使用它。偶尔,队列的lastObject将在检查queue.operations.count后消失。这很少见,但它发生了。我不确定如何解决它。 –

+0

好点我没有看到,但是:如果在队列运行时检查operations.count,它可能会在我们输入if条件时结束。我相信编辑后的表格更好更安全:(这里的测试不会崩溃,而尝试暂停队列的测试不起作用(未显示))。 – Daniel

+0

这也是我也参与的解决方案;创建一个局部变量,防止NSOperation被释放。 –