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.
如果您需要按特定顺序执行您的操作,因为其中一个操作依赖于另一个操作,您应该使用 NSOperation
的 addDependency:
方法。
所以你应该这样做:
[firstOperation addDependency: secondOperation];
这将保证 firstOperation
将在 secondOperation
之后执行。
在我的例子中,属性 operation.qualityOfService
比 operation.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
我正在研究 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.
如果您需要按特定顺序执行您的操作,因为其中一个操作依赖于另一个操作,您应该使用 NSOperation
的 addDependency:
方法。
所以你应该这样做:
[firstOperation addDependency: secondOperation];
这将保证 firstOperation
将在 secondOperation
之后执行。
在我的例子中,属性 operation.qualityOfService
比 operation.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