5

I 2 NSOperations를 포함하고 (동작들의문제 큐 동시 및 비 동시 NSOperations

하나는 표준 비 동시 운전이 setMaxConcurrentOperationCount 1로 설정하여 다른 후에 하나를 수행하도록 설정하는 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에 의해

이 두 귀중한 기사는 동시 훌륭한 세부 사항에서 작업뿐만 아니라 실행 루프를 필요로 비동기 적으로 일을 호출 할 때 스노우 레오파드 (Snow Leopard) & 아이폰 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:의 언급을 볼 수 있습니까 작업이 올바른 순서로 실행되도록합니다.

요약하면 두 번째 작업은 첫 번째 작업에 따라 다릅니다.

+0

아, 네 내가 큐 코드에 대해 언급하는 것을 잊었다 :

그것의 요점은 우리가 주 스레드에서 호출 start 방법 있도록하는 것입니다. 나는 어떤 의존성도 추가하지 않았지만 어떤 이유로 든 그렇게하지 않았습니다. 모든 작업은 관련이 없지만, 나는 하나씩 차례대로 실행하기를 원합니다. 의존성이 내가 가진 문제에 어떤 변화를 가져올 것입니까? setMaxConcurrentOperationCount를 1로 설정하고 충분하다고 생각했습니다. 귀하의 회신에 감사드립니다! –

+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 개의 흥미로운 기사를 제대로 다시 읽도록 안내해 주셨습니다! 나는 다른 사람들이이 문제를 가지고있을 때 분명하지만 시간을내어 주셔서 감사 드리며 실행 루프에 대해 죽었습니다! –