2009-10-29 26 views
5

私は2 NSOperationsが含まれており、(操作の1つは、標準的な非同時動作である問題キューの同時&非並行NSOperations

1に setMaxConcurrentOperationCountを設定することで、次々にそれらを実行するように設定されているNSOperationQueueを持っていますちょうど mainメソッド)は、ウェブからいくつかのデータを同期的に検索します(もちろん別の操作スレッド上にあります)。他の操作は、非同期に実行する必要のあるコードを使用する必要があるため、並行操作です。

問題は、並列処理が最初にキューに追加された場合にのみ動作することが判明したことです。それが非並行操作の後に来るならば、不思議なことにstartメソッドがうまく呼び出されますが、そのメソッドが終了してコールバックメソッドへの接続が確立された後、メソッドは終了しません。後でキュー内の操作は実行されません。これは、startメソッドが返った後にハングアップしているかのようであり、任意のURL接続からのコールバックは呼び出されません!

私の並行操作が最初にキューに置かれていれば、すべて正常に動作し、非同期コールバックが機能し、完了後に後続の操作が実行されます。私は全く理解していない!

以下の並行NSOperationのテストコードを見ることができます。これは確かに固いものです。

ご協力いただければ幸いです!

メインスレッド観測:

は、私はちょうど同時操作がキューに最初のものであるならば、[start]メソッドがメインスレッドで呼び出されることを発見しました。ただし、キューに最初に存在しない場合(同時または非同時のいずれかの後にある場合)、[start]メソッドはメインスレッドで呼び出されません。これは私の問題のパターンに合っているので、これは重要と思われます。これの理由は何でしょうか?

同時NSOperationコード:

@interface ConcurrentOperation : NSOperation { 
    BOOL executing; 
    BOOL finished; 
} 
- (void)beginOperation; 
- (void)completeOperation; 
@end 

@implementation ConcurrentOperation 
- (void)beginOperation { 
    @try { 

     // Test async request 
     NSURLRequest *r = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]]; 
     NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:r delegate:self]; 
     [r release]; 

    } @catch(NSException * e) { 
     // Do not rethrow exceptions. 
    } 
} 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
    NSLog(@"Finished loading... %@", connection); 
    [self completeOperation]; 
} 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
    NSLog(@"Finished with error... %@", error); 
    [self completeOperation]; 
} 
- (void)dealloc { 
    [super dealloc]; 
} 
- (id)init { 
    if (self = [super init]) { 

     // Set Flags 
     executing = NO; 
     finished = NO; 

    } 
    return self; 
} 
- (void)start { 

    // Main thread? This seems to be an important point 
    NSLog(@"%@ on main thread", ([NSThread isMainThread] ? @"Is" : @"Not")); 

    // Check for cancellation 
    if ([self isCancelled]) { 
     [self completeOperation]; 
     return; 
    } 

    // Executing 
    [self willChangeValueForKey:@"isExecuting"]; 
    executing = YES; 
    [self didChangeValueForKey:@"isExecuting"]; 

    // Begin 
    [self beginOperation]; 

} 

// Complete Operation and Mark as Finished 
- (void)completeOperation { 
    BOOL oldExecuting = executing; 
    BOOL oldFinished = finished; 
    if (oldExecuting) [self willChangeValueForKey:@"isExecuting"]; 
    if (!oldFinished) [self willChangeValueForKey:@"isFinished"]; 
    executing = NO; 
    finished = YES; 
    if (oldExecuting) [self didChangeValueForKey:@"isExecuting"]; 
    if (!oldFinished) [self didChangeValueForKey:@"isFinished"]; 
} 

// Operation State 
- (BOOL)isConcurrent { return YES; } 
- (BOOL)isExecuting { return executing; } 
- (BOOL)isFinished { return finished; } 

@end 

キューコード

// Setup Queue 
myQueue = [[NSOperationQueue alloc] init]; 
[myQueue setMaxConcurrentOperationCount:1]; 

// Non Concurrent Op 
NonConcurrentOperation *op1 = [[NonConcurrentOperation alloc] init]; 
[myQueue addOperation:op1]; 
[op1 release]; 

// Concurrent Op 
ConcurrentOperation *op2 = [[ConcurrentOperation alloc] init]; 
[myQueue addOperation:op2]; 
[op2 release]; 

答えて

10

私は問題が何かを発見しました!

デイブDribinことによって、これらの2つの貴重記事は、同時非常に詳細での操作と同様に、実行ループを必要と非同期で物事を呼び出すときにSnow Leopardの& iPhone SDKを導入していることの問題について説明します。あまりにも正しい方向に私を指しているためクリス・スーターへ

http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/ http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/

ありがとう!私はキューコードについて言及するのを忘れてしまったああ

- (void)start { 

    if (![NSThread isMainThread]) { // Dave Dribin is a legend! 
     [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO]; 
     return; 
    } 

    [self willChangeValueForKey:@"isExecuting"]; 
    _isExecuting = YES; 
    [self didChangeValueForKey:@"isExecuting"]; 

    // Start asynchronous API 

} 
+0

私も同じ問題を抱えています。しかし、私のケースでは、startメソッドは時々、新たに追加された操作を呼び出さない。キューにはステータス「実行中」が表示されます。したがって、上記の方法は使用できません。あなたは他の解決策を知っていますか?助けてください.. –

0

するための前提条件のように思われ、私は気付かなかった、また私はaddDependency:の一切の言及を見ています操作を適切な順序で実行する。要するに、第2の操作は第1の操作に依存する。

+0

それの核心は、私たちはメインスレッドで呼び出さstart方法ようにすることです。私は依存関係を追加していませんが、実際には何の理由もありません。すべての操作は無関係ですが、私はちょうどそれらが相次いで実行するようにします。依存関係は、私が抱えている問題に何らかの違いをもたらすでしょうか?私は1にsetMaxConcurrentOperationCountを設定して、それが十分であると思った。お返事をありがとうございます! –

+0

私は依存関係を追加しただけで、私の問題には何の影響もありませんでした。 –

6

問題はNSURLConnectionで発生する可能性があります。 NSURLConnectionは、特定のモード(通常はデフォルトのもの)を実行する実行ループに依存します。

あなたの問題への解決策の数があります。

  1. は、この操作は本体のみのスレッドで実行されていることを確認してください。 OS Xでこれを実行していた場合は、すべての実行ループモード(モーダルモードやイベントトラッキングモードなど)で必要な機能を実行しているかどうかを確認する必要がありますが、iPhoneで何が行われているのか分かりません。

  2. 独自のスレッドを作成して管理します。素晴らしい解決策ではありません。

  3. [NSURLConnection scheduleInRunLoop:forMode:]を呼び出し、あなたが知っているメインスレッドまたは別のスレッドを渡します。これを行うと、おそらく - [NSURLConnection unscheduleInRunLoop:forMode:]を呼び出すことをお勧めします。そうしないと、複数のスレッドでデータを受け取ることができます(少なくともドキュメントが示唆しているようです)。

  4. + [NSData dataWithContentsOfURL:options:error:]のようなものを使用すると、代わりに非同期操作にすることができるため操作が簡単になります。

  5. #4のバリアント:+ [NSURLConnection sendSynchronousRequest:returningResponse:error:]を使用します。

もしあなたがそれを得ることができるなら、#4または#5を実行します。

+0

お返事ありがとうございます。投稿したコードは、テスト目的のために作成した単純な例です。実際のプログラムは、私のコントロールの外にあるAPIを使用しており、本質的に非同期です。 問題を解決した2つの興味深い記事を正しく読み直してくれました。私はそれに答えを投稿するので、他の人が問題を抱えているのは明らかですが、時間をとってくれてありがとう、あなたは実行ループについて死んでいるのです! –