11

我创建了一个测试项目,我在其中测试我的假设NSOperationNSOperationQueue然后在我的主项目中使用它们。

我的代码非常简单,所以我将在此处包含所有代码。这是使用打开 ARC 的命令行 Foundation 项目。

操作.h

#import <Foundation/Foundation.h>

@interface Operation : NSOperation

@property (readwrite, strong) NSString *label;

- (id)initWithLabel: (NSString *)label;

@end

操作.m

#import "Operation.h"

@implementation Operation

- (void)main
{
    NSLog( @"Operation %@", _label);
}

- (id)initWithLabel: (NSString *)label
{
    if (( self = [super init] )) {
        _label = label;
    }
    return self;
}

@end

主程序

#import <Foundation/Foundation.h>
#import "Operation.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {

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

        id create = [[Operation alloc] initWithLabel: @"create"];
        id update1 = [[Operation alloc] initWithLabel: @"update1"];
        id update2 = [[Operation alloc] initWithLabel: @"update2"];

        [update1 addDependency: create];
        [update2 addDependency: create];

        [queue addOperation: update1];
        [queue addOperation: create];
        [queue addOperation: update2];

        [queue waitUntilAllOperationsAreFinished];

    }
    return 0;
}

我的输出如下所示:

2012-05-02 11:37:08.573 QueueTest[1574:1b03] Operation create
2012-05-02 11:37:08.584 QueueTest[1574:1903] Operation update2
2012-05-02 11:37:08.586 QueueTest[1574:1b03] Operation update1

写完这个并尝试了一些组合后,我发现当我像这样重新排序队列设置时:

    [queue addOperation: update1];
    [queue addOperation: create];
    [queue addOperation: update2];

    [update1 addDependency: create];
    [update2 addDependency: create];

    [queue waitUntilAllOperationsAreFinished];

我得到了相同的输出:

2012-05-02 11:38:23.965 QueueTest[1591:1b03] Operation create
2012-05-02 11:38:23.975 QueueTest[1591:1b03] Operation update1
2012-05-02 11:38:23.978 QueueTest[1591:1903] Operation update2

我应该注意到,我在一些运行中发现 update2 在 update1 之前执行,但这种行为并不奇怪。NSOperationQueue当我没有要求它是确定性时,为什么应该是确定性的?

确实感到惊讶的是,即使在添加依赖项之前将所有内容都添加到队列中,create 总是在 update1 和 update2 之前执行。

显然,这是一件愚蠢的事情,但它让我想知道:我将操作添加到队列和执行它之间的延迟是否记录在案,或者以任何方式可预测?究竟什么时候NSOperationQueue开始处理添加的操作?

真的,最重要的是,究竟是什么在NSOperationQueue等待,什么时候等待以某种我无法防御的方式咬我?

4

2 回答 2

17

显然,这是一件愚蠢的事情,但它让我想知道:我将操作添加到队列和执行它之间的延迟是否记录在案,或者以任何方式可预测?NSOperationQueue 究竟什么时候开始处理添加的操作?

后:

  • 您将操作添加到队列中,并且
  • 所有操作的依赖项都已完成,并且
  • 队列(就优先级而言)没有更好的事情可做。

对于没有不满足依赖关系的操作,这可能意味着立即。

进一步的实验似乎表明,一旦当前线程通过 [queue waitUntilAllOperationsAreFinished]、[NSThread sleepForTimeInterval: 0.000001] 或中断的线程,NSOperationQueue 就会开始运行操作。

这个假设是虚假的,原因有两个:

  1. 抢占式多任务处理,在每个版本的 OS X(以及每个版本的 iOS)上都完成,这意味着调度程序可以随时中断您的线程。你不需要放弃控制来失去控制。
  2. 每个当前的 OS X 和 iOS 设备都具有多个内核,这意味着操作可以在返回之前addOperation:开始(尽管这种极端不太可能仅仅因为开销)。更重要的是,其他操作可能能够在第一个操作时运行。

这两点,尤其是后者,也意味着“秩序”的问题几乎沦为无稽之谈。您在依赖树中安排的操作将按该树的顺序开始,否则根本没有顺序;您应该假设它们之间没有依赖关系的操作将同时运行,而不是一个接一个地运行。

启动操作后,向其添加依赖项无效。由于这可以在将其添加到队列后立即发生,因此如果您希望依赖项可靠地生效,则必须在将操作添加到队列之前将依赖项添加到操作中。

于 2012-05-02T21:04:39.953 回答
1

进一步的实验似乎表明 NSOperationQueue 只要当前线程通过 、 或中断的线程将控制权交给它就开始运行[queue waitUntilAllOperationsAreFinished]操作[NSThread sleepForTimeInterval: 0.000001]

(如果有人有更好的答案,我很乐意听到并接受它。)

于 2012-05-02T20:03:23.507 回答