Development Tip

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

yourdevel 2020. 10. 6. 19:30
반응형

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


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

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

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

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

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


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

다음 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];

Edit: Why I am using __weak reference for the Operations, can be found here. But in a nutshell is to avoid retain cycles.


If you want to perform the database writing operation in the background thread you need to create a NSManagedObjectContext for that thread.

You can create the background NSManagedObjectContext in the start method of your relevant NSOperation subclass.

Check the Apple docs for Concurrency with Core Data.

You can also create an NSManagedObjectContext that executes requests in its own background thread by creating it with NSPrivateQueueConcurrencyType and performing the requests inside its performBlock: method.


From NSOperationQueue

In iOS 4 and later, operation queues use Grand Central Dispatch to execute operations. Prior to iOS 4, they create separate threads for non-concurrent operations and launch concurrent operations from the current thread.

So,

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

In your case, you might want to create your own "database thread" by subclassing NSThread and send messages to it with performSelector:onThread:.


The execution thread of NSOperation depends on the NSOperationQueue where you added the operation. Look out this statement in your code -

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

All this assumes you have not done any further threading in main method of NSOperation which is the actual monster where the work instructions you have (expected to be) written.

However, in case of concurrent operations, the scenario is different. The queue may spawn a thread for each concurrent operation. Although it's not guarrantteed and it depends on system resources vs operation resource demands at that point in the system. You can control concurrency of operation queue by it's maxConcurrentOperationCount property.

EDIT -

I found your question interesting and did some analysis/logging myself. I have NSOperationQueue created on main thread like this -

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

And then, I went on to create an NSOperation and added it using addOperation. In the main method of this operation when i checked for current thread,

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

it was not as main thread. And, found that current thread object is not main thread object.

So, custom creation of queue on main thread (with no concurrency among its operation) doesn't necessarily mean the operations will execute serially on main thread itself.


The summary from the docs is operations are always executed on a separate thread (post iOS 4 implies GCD underlying operation queues).

It's trivial to check that it is indeed running on a non-main thread:

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

When running in a thread it's trivial to use GCD/libdispatch to run something on the main thread, whether core data, user interface or other code required to run on the main thread:

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

If you're doing any non-trivial threading, you should use FMDatabaseQueue.

참고URL : https://stackoverflow.com/questions/19569244/nsoperation-and-nsoperationqueue-working-thread-vs-main-thread

반응형