【发布时间】:2012-11-14 03:51:03
【问题描述】:
我有一个应用程序,其中一个长时间运行的进程(> 1 分钟)被放置到 NSOperationQueue(队列 A)上。当队列 A 操作运行时,UI 完全响应,正如预期的那样。
但是,我有一种用户可以执行的不同类型的操作,它在完全独立的 NSOperationQueue(队列 B)上运行。
当 UI 事件触发在队列 B 上放置操作时,它必须等到在队列 A 上当前执行的操作完成之后。这发生在 iPod Touch (MC544LL) 上。
我希望看到的是,放置在队列 B 上的任何操作都会或多或少地立即开始与队列 A 上的操作并行执行。这是我在模拟器上看到的行为。
我的问题分为两部分:
- 我在设备上看到的行为是否符合可用文档的预期?
- 使用 NSOperation/NSOperationQueue,如何使用队列 B 上的新操作抢占队列 A 上当前正在运行的操作?
注意:通过将 GCD 队列用于队列 A/B,我可以准确地获得我想要的行为,因此我知道我的设备能够支持我正在尝试做的事情。但是,我真的非常想使用 NSOperationQueue,因为这两个操作都需要取消。
我有一个简单的测试应用程序:
ViewController 是:
//
// ViewController.m
// QueueTest
//
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) NSOperationQueue *slowQueue;
@property (strong, nonatomic) NSOperationQueue *fastQueue;
@end
@implementation ViewController
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
self.slowQueue = [[NSOperationQueue alloc] init];
self.fastQueue = [[NSOperationQueue alloc] init];
}
return self;
}
-(void)viewDidLoad
{
NSLog(@"View loaded on thread %@", [NSThread currentThread]);
}
// Responds to "Slow Op Start" button
- (IBAction)slowOpStartPressed:(id)sender {
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
[self workHard:600];
}];
[self.slowQueue addOperation:operation];
}
// Responds to "Fast Op Start" button
- (IBAction)fastOpStart:(id)sender {
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
NSLog(@"Fast operation on thread %@", [NSThread currentThread]);
}];
[self.fastQueue addOperation:operation];
}
-(void)workHard:(NSUInteger)iterations
{
NSLog(@"SlowOperation start on thread %@", [NSThread currentThread]);
NSDecimalNumber *result = [[NSDecimalNumber alloc] initWithString:@"0"];
for (NSUInteger i = 0; i < iterations; i++) {
NSDecimalNumber *outer = [[NSDecimalNumber alloc] initWithUnsignedInteger:i];
for (NSUInteger j = 0; j < iterations; j++) {
NSDecimalNumber *inner = [[NSDecimalNumber alloc] initWithUnsignedInteger:j];
NSDecimalNumber *product = [outer decimalNumberByMultiplyingBy:inner];
result = [result decimalNumberByAdding:product];
}
result = [result decimalNumberByAdding:outer];
}
NSLog(@"SlowOperation end");
}
@end
我在第一次按下“Slow Op Start”按钮后看到的输出是:大约 1 秒后按下“Fast Op Start”按钮:
2012-11-28 07:41:13.051 QueueTest[12558:907] View loaded on thread <NSThread: 0x1d51ec30>{name = (null), num = 1}
2012-11-28 07:41:14.745 QueueTest[12558:1703] SlowOperation start on thread <NSThread: 0x1d55e5f0>{name = (null), num = 3}
2012-11-28 07:41:25.127 QueueTest[12558:1703] SlowOperation end
2012-11-28 07:41:25.913 QueueTest[12558:3907] Fast operation on thread <NSThread: 0x1e36d4c0>{name = (null), num = 4}
如您所见,第二个操作直到第一个操作完成后才开始执行,尽管它们是两个独立的(并且可能是独立的)NSOperationQueues。
我已经阅读了Apple Concurrency Guide,但没有找到任何描述这种情况的信息。我还阅读了有关相关主题的两个 SO 问题(link、link),但似乎都没有触及我看到的问题的核心(先发制人)。
我尝试过的其他事情:
- 在每个 NSOperation 上设置 queuePriority
- 在每个 NSOperation 上设置 queuePriority,同时将两种类型的操作放在同一个队列中
- 将两个操作放在同一个队列中
此问题经过多次修改,可能会使某些 cmets/答案难以理解。
【问题讨论】:
-
我看到你在我输入答案时治愈了症状 :-) 我怀疑如果你需要 NSOperationQueue 的操作管理工具并且不能使用,那么将慢速操作队列串行化会满足你的需要GCD。
-
@SimonLawrence,是的 - 我们的更新跨越了路径:)。不幸的是,使用 [slowQueue setMaxConcurrentOperationCount:1] 使队列串行并没有帮助。还有其他方法吗?
-
在您问题的代码中,您将两个操作添加到同一个队列中。错字,还是你所有问题的原因?
-
我尝试过的一个变体留下的错字。将两个操作添加到相同或不同队列的行为相同。
-
嗨 Rich,您是否尝试过使用 NSOperation 子类在不使用 GCD 的情况下运行块的建议(请参阅下面的 PseudoBlockOperation)?这应该可以满足您对长时间运行操作的需求,并且可能比 NSBlockOperation 更适合这些操作。
标签: objective-c ios concurrency