5

2 つの NSOperations を含む NSOperationQueue があり、1 に設定することでそれらを次々に実行するように設定されていますsetMaxConcurrentOperationCount

操作の 1 つは、Web から一部のデータを (もちろん別の操作スレッドで) 同期的に取得する標準の非同時操作 (単なるmainメソッド) です。もう 1 つの操作は、非同期で実行する必要があるコードを使用する必要があるため、同時操作です。

問題は、同時操作が最初にキューに追加された場合にのみ機能することを発見したことです。非同時操作の後に来ると、奇妙なことに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];
4

3 に答える 3

10

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

Dave 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/

私を正しい方向に向けてくれた Chris Suter にも感謝します!

startその要点は、メインスレッドで呼び出したメソッドを確実にすることです:

- (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

}
于 2009-10-29T13:37:39.413 に答える
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を実行してください。

于 2009-10-29T11:25:11.950 に答える
0

addDependency:操作を適切な順序で実行するための前提条件のように思える.

つまり、2 番目の操作は最初の操作に依存します。

于 2009-10-29T09:54:57.980 に答える