長時間実行されるプロセス (> 1 分) が NSOperationQueue (キュー A) に配置されるアプリケーションがあります。キュー A 操作の実行中、UI は期待どおりに完全に応答します。
ただし、ユーザーが実行できる別の種類の操作があり、完全に別の NSOperationQueue (キュー B) で実行されます。
UI イベントがキュー B で操作の配置をトリガーする場合、キュー A で現在実行中の操作が終了するまで待機する必要があります。これは、iPod Touch (MC544LL) で発生します。
代わりに期待していたのは、キュー B に配置された操作は、多かれ少なかれすぐにキュー A の操作と並行して実行を開始するということでした。これが、シミュレーターで見られる動作です。
私の質問は2つの部分です:
- デバイスで見られる動作は、利用可能なドキュメントに基づいて予想されるものですか?
- NSOperation/NSOperationQueue を使用して、キュー A で現在実行中の操作を、キュー B に配置された新しい操作でプリエンプトするにはどうすればよいですか?
注: キュー A/B に GCD キューを使用することで、目的の動作を正確に取得できるため、自分のデバイスが私がやろうとしていることをサポートできることがわかります。ただし、両方の操作をキャンセル可能にする必要があるため、私は本当に NSOperationQueue を使用したいと考えています。
私は簡単なテストアプリケーションを持っています:
ビューコントローラーは次のとおりです。
//
// 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}
ご覧のとおり、2 番目の操作は、最初の操作が終了するまで実行を開始しません。これらは 2 つの別個の (おそらく独立した) NSOperationQueue であるという事実にもかかわらずです。
Apple Concurrency Guideを読みましたが、この状況を説明するものは何も見つかりません。また、関連するトピックに関する 2 つの SO の質問 ( link、link ) も読みましたが、どちらも私が見ている問題 (プリエンプション) の核心には達していないようです。
私が試した他のこと:
- 各 NSOperation に queuePriority を設定する
- 両方のタイプの操作を同じキューに配置しながら、各 NSOperation に queuePriority を設定する
- 両方の操作を同じキューに配置する
この質問は複数回編集されているため、特定のコメント/回答が理解しにくい場合があります。