NSOperation и NSOperationQueue рабочий поток против основного потока
Я должен выполнить ряд операций загрузки и записи базы данных в моем приложении. Я использую NSOperation
и NSOperationQueue
то же самое.
это сценарий применения:
- получить все почтовые индексы с места.
- для каждого почтового индекса принести все дома.
- для каждого дома принести жителя детали
как уже было сказано, я определил NSOperation
для каждой задачи. В первом случае (Task1), я посылаю запрос сервер для получения всех почтовых индексов. Делегат внутри NSOperation
получаете данные. Эти данные затем записываются в базу данных. Операция базы данных определяется в другом классе. От NSOperation
класс я делаю вызов функции записи, определенные в классе базы данных.
мой вопрос заключается в том, происходит ли операция записи базы данных в основном потоке или в фоновом потоке? Как я называл его в пределах NSOperation
Я ожидал, что он будет работать в другом потоке (не MainThread) в качестве NSOperation
. Может кто-нибудь объяснить этот сценарий, имея дело с NSOperation
и NSOperationQueue
.
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
дает более точный контроль, с тем, что вы хотите сделать. Вы можете создавать зависимости между двумя операциями (загрузка и сохранение в базу данных). Чтобы передать данные между одним блоком и другим, можно предположить, например, что aNSData
будет поступать с сервера так:__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:
метод.
в 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.