如何确保并发NSOperationQueue中的FIFO执行?

时间:2017-05-03 10:10:34

标签: objective-c nsoperation nsoperationqueue

我正在研究框架并且为了确保非阻塞公共方法,我使用NSOperationQueue将所有公共方法调用放入操作队列并立即返回。

不同操作之间没有关系或依赖关系,唯一重要的是操作以FIFO顺序启动,其顺序与添加到队列的顺序相同。

以下是我当前实施的示例(sample project here):

@implementation Executor

-(instancetype) init {
    self = [super init];
    if(self) {
        _taskQueue = [[NSOperationQueue alloc] init];
        _taskQueue.name = @"com.d360.tasks";

    }
    return self;
}

-(void) doTask:(NSString*) taskName
{
    NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"executing %@", taskName);
    }];

    [self.taskQueue addOperation:operation];
}

我意识到操作的启动顺序不一定是它们添加到队列的顺序。例如,如果我打电话

[self.executor doTask:@"Task 1"];
[self.executor doTask:@"Task 2"];

有时候Task 2会在Task 1之后启动。

问题是如何确保FIFO执行开始

我可以使用_taskQueue.maxConcurrentOperationCount = 1;来实现它,但这只允许我一次只能操作一次,我不想要。一个操作不应该阻止任何其他操作,只要它们以正确的顺序启动它们就可以并发运行。

我还查看了NSOperationQueuePriority属性,如果我知道电话的优先级,我就不会这样做。实际上,即使我将先前添加的操作发送到NSOperationQueuePriorityHigh而第二个添加到NSOperationQueuePriorityNormal,也不保证订单。

[self.executor doTask:@"Task 1" withQueuePriority:NSOperationQueuePriorityHigh];
[self.executor doTask:@"Task 2" withQueuePriority:NSOperationQueuePriorityNormal];

输出有时是

executing Task 2
executing Task 1

有什么想法吗?

感谢, 扬

1 个答案:

答案 0 :(得分:1)

创建每个任务时,您可以使用NSOperation -addDependency添加对上一个任务的依赖关系。复杂的是在依赖任务完成之前不满足依赖性,这可能不是你想要的。您可以通过在每个任务中创建另一个NSOperation来解决这个问题,并使下一个排队的任务依赖于那个。这个内部任务可以设置一个标志或者说“嘿,我已经开始!”的东西。然后,当该内部任务完成时,它将满足队列中下一个任务的依赖性并允许它开始。

看起来似乎是一种令人费解的做事方式,而且我不确定这种好处是否值得额外的复杂化 - 为什么操作的起始顺序是重要的,如果它们真的是独立的操作?一旦它们启动,操作系统就决定哪个任务获得CPU时间,并且你无论如何都没有太多的控制权,那么为什么不将它们排队并让操作系统管理启动命令呢?