NSOperation и NSOperationQueue рабочий поток против основного потока


Я должен выполнить ряд операций загрузки и записи базы данных в моем приложении. Я использую NSOperation и NSOperationQueue то же самое.

это сценарий применения:

  • получить все почтовые индексы с места.
  • для каждого почтового индекса принести все дома.
  • для каждого дома принести жителя детали

как уже было сказано, я определил NSOperation для каждой задачи. В первом случае (Task1), я посылаю запрос сервер для получения всех почтовых индексов. Делегат внутри NSOperation получаете данные. Эти данные затем записываются в базу данных. Операция базы данных определяется в другом классе. От NSOperation класс я делаю вызов функции записи, определенные в классе базы данных.

мой вопрос заключается в том, происходит ли операция записи базы данных в основном потоке или в фоновом потоке? Как я называл его в пределах NSOperation Я ожидал, что он будет работать в другом потоке (не MainThread) в качестве NSOperation. Может кто-нибудь объяснить этот сценарий, имея дело с NSOperation и NSOperationQueue.

6 77

6 ответов:

мой вопрос заключается в том, происходит ли операция записи базы данных в main потоке или в фоновом потоке?

при создании 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: почему я использую __weak ссылка на операции, можно найти здесь. Но в двух словах, чтобы избежать удержания циклов.

если вы хотите выполнить операцию записи базы данных в фоновом потоке, вам нужно создать NSManagedObjectContext на что-нить.

вы можете создать фон NSManagedObjectContext в методе запуска вашего соответствующего NSOperation подкласс.

проверьте документы Apple для параллелизм с основными данными.

вы также можете создать NSManagedObjectContext который выполняет запросы в своем собственном фоновом потоке, создавая его с помощью NSPrivateQueueConcurrencyType и выполнение запросов внутри его performBlock: метод.

С 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:.

поток выполнения NSOperation зависит от NSOperationQueue где вы добавили операцию. Обратите внимание на это утверждение в вашем коде -

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

все это предполагает, что вы не сделали никаких дальнейших потоков в main метод NSOperation который является фактическим монстром, где рабочие инструкции, которые вы (как ожидается) написали.

однако, в случае параллельных операций, сценарий отличается. Очередь может порождать поток для каждой параллельной операции. Хотя это не guarrantteed, и это зависит от системных ресурсов и требований к операционным ресурсам в этот момент в системе. Вы можете управлять параллелизмом очереди операций с помощью it's maxConcurrentOperationCount собственность.

EDIT -

Я нашел ваш вопрос интересным и сделал некоторый анализ/протоколирование себя. У меня есть 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]);

это было не как основной поток. И обнаружил, что текущий объект потока не является основным объектом потока.

таким образом, пользовательское создание очереди в основном потоке (без параллелизма среди его операций) не обязательно означает, что операции будут выполняться последовательно в самом основном потоке.

резюме из документов 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
});

Если вы делаете какие-либо нетривиальные потоки, вы должны использовать FMDatabaseQueue.