NSOperation 队列优先级未按预期工作

NSOperation Queue Priority Does Not Work As Expected

我正在研究 NSOperation class 并尝试模拟一种情况,当我需要管理操作的优先级时。

请考虑以下代码:

@implementation MyOperation // First custom NSOperation class


- (void)main {

    @autoreleasepool {

        NSLog(@"First operation start"); 
    }
}

@implementation MySecondOperation // Second custom NSOperation class

- (void)main {

    @autoreleasepool {

        NSLog(@"Second operation start");
    }
}

@implementation ViewController // Other class using NSOperations

- (void)viewDidLoad {

    NSOperationQueue *myQueue = [NSOperationQueue new];

    MyOperation *firstOperation = [MyOperation new];

    MySecondOperation *secondOperation = [MySecondOperation new];

    [firstOperation setQueuePriority:NSOperationQueuePriorityLow];
    [secondOperation setQueuePriority:NSOperationQueuePriorityHigh];

    [myQueue addOperation:secondOperation];
    [myQueue addOperation:firstOperation];

}

代码非常简单。然而,你会惊讶地看到输出:

2015-08-12 20:32:09.433 NSOperationTesting[1181:39809] First operation start
2015-08-12 20:32:09.433 NSOperationTesting[1181:39811] Second operation start

优先级较低的NSOperation在操作前启动,优先级较高。我知道,NSOperationQueue 按 FIFO 顺序工作,但是,我专门设置了两个操作的优先级,那么为什么第一个操作先完成?它应该在第二个之后(具有更高的优先级)。

尝试将 myQueue.maxConcurrentOperationCount 设置为 1。我的猜测是 运行 您的操作并发。

不信任NSLog。您只添加了两个操作,并且它们是同时并发执行的。我已经修改 运行 你的代码,下面的例子表明高优先级操作在低优先级操作之前执行。

NSOperationQueue *myQueue  = [NSOperationQueue new];
NSMutableArray *operations = [NSMutableArray new];
for (int i = 0; i < 10000; i++) {
    MyOperation *firstOperation = [MyOperation new];

    MySecondOperation *secondOperation = [MySecondOperation new];

    [firstOperation setQueuePriority:NSOperationQueuePriorityLow];
    [secondOperation setQueuePriority:NSOperationQueuePriorityHigh];

    [operatons addObject:firstOperation];
    [operatons addObject:secondOperation];
}

[myQueue addOperations:operations waitUntilFinished:NO];

输出:

2015-08-12 19:47:30.826 oper[1137:31464] Second operation start
2015-08-12 19:47:30.826 oper[1137:31487] Second operation start
2015-08-12 19:47:30.826 oper[1137:31465] Second operation start
...
2015-08-12 19:47:36.443 oper[1137:31491] First operation start
2015-08-12 19:47:36.443 oper[1137:31491] First operation start
2015-08-12 19:47:36.443 oper[1137:31491] First operation start

编辑: 正如 Rob 所说,NSOperation 优先级不保证 运行 执行顺序。如果您想等到一个操作完成后再开始另一个操作,请使用 addDependency: 方法。

您不应使用或依赖 QueuePriority 来设置执行您的 operations 的特定顺序,因为您无法保证您的操作将按优先顺序执行。

根据 documentation:

You should use priority values only as needed to classify the relative priority of non-dependent operations. Priority values should not be used to implement dependency management among different operation objects. If you need to establish dependencies between operations, use the addDependency: method instead.

如果您需要按特定顺序执行您的操作,因为其中一个操作依赖于另一个操作,您应该使用 NSOperationaddDependency: 方法。 所以你应该这样做:

[firstOperation addDependency: secondOperation];

这将保证 firstOperation 将在 secondOperation 之后执行。

在我的例子中,属性 operation.qualityOfServiceoperation.queuePriority 给了我更好的结果。您可能想尝试一下。

我将 operation.qualityOfService = NSQualityOfServiceUserInteractive 用于高优先级操作,将 operation.qualityOfService = NSQualityOfServiceDefault 用于普通优先级操作,它按预期工作。

您可以使用以下代码进行测试。

#define kMaxSeconds 5
#define kSleepTime 1

@interface ViewController ()
@property (nonatomic, assign) NSInteger currentCount;
@property (nonatomic, strong) NSOperationQueue* operationQueue;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.currentCount = 0;

    [self addOperationInQueueWithQuality:NSQualityOfServiceUserInteractive];
    [self addOperationInQueueWithQuality:NSQualityOfServiceDefault];
    [self addOperationInQueueWithQuality:NSQualityOfServiceUserInteractive];
    [self addOperationInQueueWithQuality:NSQualityOfServiceDefault];
    [self addOperationInQueueWithQuality:NSQualityOfServiceUserInteractive];
}

- (void)addOperationInQueueWithQuality:(NSQualityOfService)quality{
    if(!self.operationQueue){
        self.operationQueue = [NSOperationQueue new];
    }
    self.currentCount++;
    NSInteger blockNumber = self.currentCount;
    NSBlockOperation* blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        for(int i=0; i < kMaxSeconds; i++){
            NSMutableString* str = [NSMutableString new];
            for(int j = 0; j < self.currentCount+1; j++){
                if(j == blockNumber){
                    [str appendString:@"    |"];
                }else{
                    [str appendString:@"    ."];
                }
            }
            NSLog(@"%@", [str copy]);
            sleep(kSleepTime);
        }
    }];
    blockOperation.qualityOfService = quality;
    blockOperation.completionBlock = ^{
        NSLog(@"Block operation %ld completed", blockNumber);
    };
    [self.operationQueue addOperation:blockOperation];
}

@end