it-swarm.com.ru

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

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

Это сценарий приложения:

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

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

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

79
Zach

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

Если вы создаете 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 дает лучший контроль над тем, что вы хотите сделать. Вы можете создать зависимости между двумя операциями (загрузить и сохранить в базе данных). Чтобы передать данные между одним блоком и другим, вы можете предположить, например, что 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 для операций, можно узнать здесь . Но в двух словах стоит избегать сохранения циклов.

172
Rui Peres

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

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

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

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

16
serrrgi

От 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:.

10
ilya n.

Поток выполнения 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]);

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

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

9
Ashok

Сводка из документации: 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
});
1
duncanwilcox