5

mam NSOperationQueue zawierającym 2 NSOperations i ma wykonać je jeden po drugim poprzez ustawienie setMaxConcurrentOperationCount do 1.Problemy kolejkowania współbieżne i nierównoległymi NSOperations

jedną z operacji, to standardowa operacja nierównoległe (tylko metoda main), która synchronicznie pobiera niektóre dane z sieci (oczywiście w oddzielnym wątku operacji). Druga operacja jest operacją współbieżną, ponieważ potrzebuję użyć kodu, który musi działać asynchronicznie.

Problem polega na tym, że odkryłem, że operacja współbieżna działa tylko wtedy, gdy jest najpierw dodana do kolejki. Jeśli przychodzi po jakichkolwiek operacjach innych niż współbieżne, to dziwnie, że metoda start zostaje wywołana dobrze, ale po tym, jak ta metoda się kończy i skonfigurowałem swoje połączenie do wywołania zwrotnego metodą, nigdy tego nie robi. Żadne dalsze operacje w kolejce nie zostaną wykonane później. To tak, jakby zawiesza się po powrocie metody start i nie wywoływane są żadne połączenia zwrotne z jakichkolwiek połączeń URL!

Jeśli moja współbieżna operacja zostanie umieszczona jako pierwsza w kolejce, wszystko działa poprawnie, asynchroniczne wywołania zwrotne działają, a kolejna operacja jest wykonywana po jej zakończeniu. Nie rozumiem w ogóle!

Możesz zobaczyć kod testu dla mojej równoległej NSOperation poniżej, i jestem prawie pewien, że jest solidny.

Każda pomoc będzie bardzo ceniona!

główna Obserwacja Temat:

właśnie odkrył, że jeśli jednoczesne działanie jest pierwszy w kolejce po czym metoda [start] jest wywoływana w głównym wątku. Jeśli jednak nie jest pierwszym w kolejce (jeśli jest po współbieżnym lub nie współbieżnym), to metoda [start] nie jest wywoływana w głównym wątku. Wydaje się to ważne, ponieważ pasuje do wzorca mojego problemu. Co mogłoby być tego przyczyną?

Jednoczesne Kod 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 

Kod kolejkowania

// 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]; 

Odpowiedz

10

Odkryłem, jaki był problem!

Te dwa bezcenne artykuły autorstwa Dave'a Dribin opisać jednoczesnych operacji w najdrobniejszych szczegółach, a także problemy, które Snow Leopard & iPhone SDK wprowadzenia Dzwoniąc asynchronicznie rzeczy, które wymagają pętlę run.

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/

Dzięki Chris Suter zbyt dla wskazujące mnie we właściwym kierunku!

Istotą jest to, aby upewnić się, że metoda start nas wezwał głównego wątku:

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

Mam również ten sam problem. Ale w moim przypadku metoda start również nie wymagała czasami nowych operacji. Kolejka nadal pokazuje status "Running". Tak więc powyższej metody nie można użyć. Czy znasz jakieś inne rozwiązanie? Proszę pomóż.. –

0

nie zauważyłem, ani nie widzę żadnej wzmianki addDependency:, które mogłyby wydawać się warunek wstępny wykonanie operacji do wykonania we właściwej kolejności.

Krótko mówiąc, druga operacja zależy od pierwszej.

+0

Aha zapomniałem wspomnieć o kodzie kolejki. Nie dodałem żadnych zależności, ale nie z jakiegoś powodu. Wszystkie operacje są niezwiązane, ale chcę, żeby były uruchamiane jedna po drugiej. Czy zależności będą miały wpływ na problem, który mam? Ustawiłem setMaxConcurrentOperationCount na 1 i myślałem, że to wystarczy. Dzięki za twoją odpowiedź! –

+0

Właśnie dodałem zależności i nie miało to żadnego wpływu na mój problem. –

6

Twój problem jest najprawdopodobniej związany z NSURLConnection. NSURLConnection zależy od uruchomienia pętli działającej w określonym trybie (zwykle tylko domyślnym).

Istnieje szereg rozwiązań problemu:

  1. Upewnij się, że operacja ta przebiega tylko na głównym wątku. Jeśli robiłeś to w systemie OS X, chciałbyś sprawdzić, czy robi to, co chcesz, we wszystkich trybach uruchamiania pętli (na przykład modów i trybów śledzenia zdarzeń), ale nie wiem, co to jest umowa na iPhone'a.

  2. Twórz własne wątki i zarządzaj nimi. To niezłe rozwiązanie.

  3. Zadzwoń - [NSURLConnection scheduleInRunLoop: forMode:] i podaj główny wątek lub inny wątek, o którym wiesz. Jeśli to zrobisz, prawdopodobnie chcesz zadzwonić - [NSURLConnection unscheduleInRunLoop: forMode:] pierwszy inaczej można odbierać dane w wielu wątkach (lub przynajmniej to, co wydaje się sugerować dokumentacja).

  4. Użyj czegoś takiego jak + [NSData dataWithContentsOfURL: options: error:], a to również uprości twoją operację, ponieważ możesz zamiast tego zrobić operację inną niż współbieżna.

  5. Wariant na # 4: użyj + [NSURLConnection sendSynchronousRequest: returningResponse: error:].

Jeśli możesz uciec, wykonaj # 4 lub # 5.

+0

Dziękuję za odpowiedź. Wysłany przeze mnie kod jest prostym przykładem, który stworzyłem do celów testowych. Rzeczywisty program używa interfejsu API, który jest poza moją kontrolą i ma charakter asynchroniczny. Jednak doprowadziłeś mnie do ponownego przeczytania 2 interesujących artykułów, które rozwiązały problem! Opublikuję odpowiedź, aby było jasne, kiedy inni mają problem, ale dziękuję za poświęcenie czasu i jesteś martwy w kwestii pętli run! –

Powiązane problemy