NSOperation和NSOperationQueue工作线程vs主线程

我必须在我的应用程序中执行一系列下载和数据库写入操作。 我正在使用NSOperationNSOperationQueue相同。

这是应用场景:

  • 从一个地方获取所有邮编。
  • 为每个邮政编码提取所有房屋。
  • 为每个房子获取居民的细节

如上所述,我已经为每个任务定义了一个NSOperation 。 在第一种情况下(Task1),我发送一个请求到服务器来获取所有的邮编。 NSOperation内的NSOperation将收到数据。 这些数据被写入数据库。 数据库操作是在不同的类中定义的。 从NSOperation类我正在调用数据库类中定义的写函数。

我的问题是数据库写操作是在主线程还是在后台线程中发生? 当我在一个NSOperation调用它时,我期待它在NSOperation中运行在不同的线程(Not MainThread)中。 有人可以在处理NSOperationNSOperationQueue时解释这种情况。

我的问题是数据库写操作是在主线程还是在后台线程中发生?

如果你从头开始创build一个NSOperationQueue ,如下所示:

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

它将在后台线程中:

操作队列通常提供用于运行其操作的线程。 在OS X v10.6及更高版本中,操作队列使用libdispatch库(也称为Grand Central Dispatch)来启动其操作的执行。 因此,操作总是在单独的线程上执行 ,而不pipe它们是被指定为并发还是非并发操作

除非你使用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更好地控制你想要做什么。 您可以在两个操作之间创build依赖关系(下载并保存到数据库)。 要在一个块和另一个块之间传递数据,你可以假设一个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参考的操作,可以在这里find。 但简而言之就是避免保留周期。

如果要在后台线程中执行数据库写入操作,则需要为该线程创build一个NSManagedObjectContext

你可以在相关NSOperation子类的start方法中创build背景NSManagedObjectContext

核心数据检查苹果文档的并发性。

你也可以创build一个NSManagedObjectContext ,它通过在NSPrivateQueueConcurrencyType创build它并在其performBlock:方法内执行请求,在自己的后台线程中执行请求。

NSOperation的执行线程取决于添加操作的NSOperationQueue 。 看看你的代码中的这个声明 –

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

所有这一切都假定你没有对NSOperation main方法做进一步的线程化处理,这个方法就是你所写的工作指令的实际怪物。

但是,在并发操作的情况下,情况是不同的。 队列可能为每个并发操作产生一个线程。 虽然没有保证,但依赖于系统资源需求与系统当时的运营资源需求。 您可以通过它的maxConcurrentOperationCount属性来控制操作队列的maxConcurrentOperationCount性。

编辑

我发现你的问题很有趣,我自己做了一些分析/logging。 我有像这样在主线程上创buildNSOperationQueue –

 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 

然后,我继续创build一个NSOperation并使用addOperation添加它。 在这个操作的主要方法,当我检查当前线程,

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

这不是主线。 而且,发现当前线程对象不是主线程对象。

所以,在主线程上自定义创build队列(在其操作中没有并发)并不一定意味着操作将在主线程本身上串行执行。

从NSOperationQueue

在iOS 4及更高版本中,操作队列使用Grand Central Dispatch来执行操作。 在iOS 4之前,他们为非并发操作创build单独的线程,并从当前线程启动并发操作。

所以,

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

在你的情况下,你可能想创build自己的“数据库线程”通过NSThread并发送消息给它与performSelector:onThread:

文档中的总结是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 。