NSOperation 및 NSOperationQueue 작업 스레드 대 기본 스레드


81

내 앱에서 일련의 다운로드 및 데이터베이스 쓰기 작업을 수행해야합니다. 내가 사용하고 NSOperationNSOperationQueue동일합니다.

이것은 애플리케이션 시나리오입니다.

  • 장소에서 모든 우편 번호를 가져옵니다.
  • 각 우편 번호에 대해 모든 집을 가져옵니다.
  • 각 집에 대해 주민 정보를 가져옵니다.

말했듯 NSOperation이 각 작업에 대해 정의했습니다 . 첫 번째 경우 (Task1)에서는 모든 우편 번호를 가져 오기 위해 서버에 요청을 보냅니다. 의 대리인 NSOperation이 데이터를받습니다. 이 데이터는 데이터베이스에 기록됩니다. 데이터베이스 작업이 다른 클래스에 정의되어 있습니다. 에서 NSOperation클래스 I 데이터베이스 클래스에 정의 된 쓰기 함수에 대한 호출을하고있다.

내 질문은 데이터베이스 쓰기 작업이 주 스레드에서 발생하는지 백그라운드 스레드에서 발생하는지 여부입니다. 내에서 호출했을 NSOperation때 다른 스레드 (MainThread가 아님)에서 NSOperation. 누군가 NSOperation및을 ( 를) 다루는 동안이 시나리오를 설명해 주시겠습니까 NSOperationQueue?


3
메인 큐에 작업을 추가하면 메인 스레드에서 수행됩니다. 자신의 NSOperationQueue를 만들고 작업을 추가하면이 대기열의 스레드에서 수행됩니다.
Cy-4AH

1
더 구체적이거나 일부 코드를 게시하지 않으면 @ Cy-4AH가 준 것보다 더 나은 대답을 얻을 것이라고 생각하지 않습니다. 나는 당신이 항상 코드에 중단 점을 넣을 수 있습니다 말할 것이다 그것은 여행 때 무엇 추적을 스레드하기에을 보여줍니다.
브래드 알 레드

"NSOperation 내의 대리인이 데이터를 수신합니다." 평균? 대리자 속성 NSOperationNSOperationQueue포함 하지 않습니다 .
제프리 토마스

또한 현재 스레드에 대해 가정하지 않고 델리게이트 호출을 기본 스레드로 푸시 할 수도 있습니다.
Wain

답변:


175

내 질문은 데이터베이스 쓰기 작업이 주 스레드에서 발생하는지 백그라운드 스레드에서 발생하는지 여부입니다.

다음 NSOperationQueue과 같이 처음부터 만드는 경우 :

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];

백그라운드 스레드에 있습니다.

작업 대기열은 일반적으로 작업을 실행하는 데 사용되는 스레드를 제공합니다. OS X v10.6 이상에서 작업 대기열은 libdispatch 라이브러리 (Grand Central Dispatch라고도 함)를 사용하여 작업 실행을 시작합니다. 결과적으로 작업은 동시 또는 비 동시 작업으로 지정되었는지 여부에 관계없이 항상 별도의 스레드 에서 실행됩니다.

다음을 사용하지 않는 한 mainQueue:

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

다음과 같은 코드도 볼 수 있습니다.

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{

   // Background work

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // Main thread work (UI usually)
    }];
}];

그리고 GCD 버전 :

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
             {
              // Background work            
             dispatch_async(dispatch_get_main_queue(), ^(void)
              {
                   // Main thread work (UI usually)                          
              });
});

NSOperationQueue원하는 작업을보다 세밀하게 제어 할 수 있습니다. 두 작업간에 종속성을 만들 수 있습니다 (데이터베이스에 다운로드 및 저장). 한 블록과 다른 블록간에 데이터를 전달하려면 예를 들어 a NSData가 서버에서 오는 것으로 가정 할 수 있습니다 .

__block NSData *dataFromServer = nil;
NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakDownloadOperation = downloadOperation;

[weakDownloadOperation addExecutionBlock:^{
 // Download your stuff  
 // Finally put it on the right place: 
 dataFromServer = ....
 }];

NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation;

 [weakSaveToDataBaseOperation addExecutionBlock:^{
 // Work with your NSData instance
 // Save your stuff
 }];

[saveToDataBaseOperation addDependency:downloadOperation];

[myQueue addOperation:saveToDataBaseOperation];
[myQueue addOperation:downloadOperation];

편집 :__weak 작업에 대한 참조를 사용하는 이유 는 여기 에서 찾을 수 있습니다 . 그러나 간단히 말해서 유지주기를 피하는 것입니다.


3
대답은 정확하지만 올바른 코드 줄은 다음 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];과 같습니다. 주 스레드의 NSOperationQueue를 일시 중단 할 수 없습니다.
Gianluca P.

2
@ jacky-boy 흥미롭지 않은데, 왜 최종 코드 조각에서 downloadOperation 및 saveToDataBaseOperation에 대한 약한 참조를 사용합니까?
Michael Waterfall

RuiAAPeres, 왜 최종 코드 스 니펫에 약한 참조가 사용되는지 궁금합니다. 그것에 대해 좀 조명 해 주시겠습니까?
Pavan 2014 년

@Pavan의 아이디어는 자체 블록 내에서 동일한 블록 작업을 참조하면 유지주기가 발생한다는 것입니다. 참고 : conradstoll.com/blog/2013/1/19/…
Rui Peres

그러면 그 안에서 실행되는 블록과 동일한 블록은 무엇입니까?
Pavan 2014 년

16

백그라운드 스레드에서 데이터베이스 쓰기 작업을 수행하려면 NSManagedObjectContext해당 스레드에 대해 생성해야합니다 .

NSManagedObjectContext관련 NSOperation하위 클래스 의 시작 메서드에서 배경 을 만들 수 있습니다 .

핵심 데이터와의 동시성에 대한 Apple 문서를 확인하십시오 .

또한 메서드 내에서 요청 NSManagedObjectContextNSPrivateQueueConcurrencyType만들고 수행하여 자체 백그라운드 스레드에서 요청을 실행하는를 만들 수도 있습니다 performBlock:.


17
이 질문이 핵심 데이터와 관련이 있다고 생각하는 이유는 무엇입니까?
Nikolai Ruhe 2013

11

에서 NSOperationQueue

iOS 4 이상에서 작업 대기열은 Grand Central Dispatch를 사용하여 작업을 실행합니다. iOS 4 이전에는 비 동시 작업에 대해 별도의 스레드를 만들고 현재 스레드에서 동시 작업을 시작했습니다.

그래서,

[NSOperationQueue mainQueue] // added operations execute on main thread
[NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread

귀하의 경우에는, 당신은 서브 클래스에 의해 자신의 "데이터베이스 스레드를"만들 수 있습니다 NSThread와 함께 메시지를 보낼 performSelector:onThread:.


덕분에 많이 ... 유 내 인생 저장 : [NSOperationQueue 새로운] // 후 iOS4, 하지 메인 스레드 보장
ikanimo

9

NSOperation의 실행 스레드 NSOperationQueue는 작업을 추가 한 위치에 따라 다릅니다 . 당신의 코드에서이 문장을보세요-

[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class

이 모든 것은 당신이 (예상되는) 작업 지침이 쓰여진 실제 괴물 인 main방법 에서 더 이상 스레딩을 수행하지 않았다고 가정합니다 NSOperation.

그러나 동시 작업의 경우 시나리오가 다릅니다. 큐는 각 동시 작업에 대해 스레드를 생성 할 수 있습니다. 보장되지는 않지만 시스템 리소스 대 시스템의 해당 시점 에서 운영 리소스 요구 따라 다릅니다 . maxConcurrentOperationCount속성 에 따라 작업 대기열의 동시성을 제어 할 수 있습니다 .

편집 -

귀하의 질문이 흥미 롭다는 것을 알았고 직접 분석 / 로깅했습니다. 다음과 같이 주 스레드에서 NSOperationQueue를 만들었습니다.

self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease];

NSLog(@"Operation queue creation. current thread = %@ \n main thread = %@", [NSThread currentThread], [NSThread mainThread]);
self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency

그런 다음 계속해서 NSOperation을 만들고 addOperation을 사용하여 추가했습니다. 이 작업의 주요 방법에서 현재 스레드를 확인할 때

NSLog(@"Operation obj =  %@\n current thread = %@ \n main thread = %@", self, [NSThread currentThread], [NSThread mainThread]);

그것은 메인 스레드가 아닙니다. 그리고 현재 스레드 개체가 주 스레드 개체가 아님을 발견했습니다.

따라서 메인 스레드에서 큐를 사용자 지정 생성 (작업간에 동시성이 없음)한다고해서 반드시 작업이 메인 스레드 자체에서 직렬로 실행되는 것은 아닙니다.


사용 [[NSOperationQueue alloc] init]하면 대기열이 기본 기본 배경 전역 DispatchQueue를 사용하게됩니다. 따라서 사용자 정의 초기화 된 OperationQueue는 작업을 백그라운드 DispatchQueue에 공급하므로 기본 스레드가 아닌 스레드에 작업을 공급합니다. 주 스레드에 작업을 공급하려면 .NET과 같은 것을 사용하여 주 OperationQueue를 가져와야합니다 [NSOperationQueue mainQueue]. 이것은 내부적으로 기본 DispatchQueue를 사용하는 기본 작업 대기열을 가져 와서 결국 작업을 기본 스레드에 공급합니다.
Gordonium

1

문서의 요약은 다음과 같습니다 operations are always executed on a separate thread(iOS 4 이후에는 GCD 기본 작업 대기열을 의미 함).

주 스레드가 아닌 스레드에서 실제로 실행되고 있는지 확인하는 것은 간단합니다.

NSLog(@"main thread? %@", [NSThread isMainThread] ? @"YES" : @"NO");

스레드에서 실행할 때 GCD / libdispatch를 사용하여 기본 스레드에서 실행하는 데 필요한 핵심 데이터, 사용자 인터페이스 또는 기타 코드에 관계없이 기본 스레드에서 무언가를 실행하는 것은 간단합니다.

dispatch_async(dispatch_get_main_queue(), ^{
    // this is now running on the main thread
});

-2

사소하지 않은 스레딩을 수행하는 경우 FMDatabaseQueue 를 사용해야합니다 .


1
질문은 fmdb 또는 sqlite와 같은 특정 데이터베이스를 언급하지 않습니다.
Nikolai Ruhe 2013

맞습니다. 저는 상황에 따라 가정을했습니다. 앱이 MySQL과 같은 다중 테넌트의 스레드 안전 데이터베이스에 연결되어 있다면이 질문은 의미가 없습니다. 사용할 수있는 다른 단일 사용자 데이터베이스가 있지만 SQLite가 지금까지 가장 일반적입니다.
Holly
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.