NSOperation 和 NSOperationQueue 工作线程对主线程

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

这是应用场景:

  • 从一个地方获取所有的邮政编码。
  • 对于每个邮政编码获取所有房屋。
  • 对于每个房子获取居民的详细信息

如前所述,我已经为每个任务定义了一个 NSOperation。在第一种情况下(Task1) ,我向服务器发送一个请求来获取所有的邮政编码。NSOperation中的委托将接收数据。然后将此数据写入数据库。数据库操作在不同的类中定义。我从 NSOperation类调用数据库类中定义的 write 函数。

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

94134 次浏览

如果您正在执行任何非平凡的线程,那么您应该使用 FMDatabaseQueue

NSOperation 的执行线程取决于添加操作的 NSOperationQueue。在你的代码中注意这个语句

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

所有这些都假设您没有在 NSOperationmain方法中进行任何进一步的线程处理,NSOperation是您已经(预期将要)编写工作指令的实际怪物。

但是,在并发操作的情况下,情况就不同了。队列可以为每个并发操作产生一个线程。虽然这是不能保证的,它取决于系统资源相对于操作资源的要求在系统中的 那个时候。可以通过操作队列的 maxConcurrentOperationCount属性控制操作队列的并发性。

编辑部

我发现你的问题很有趣,我自己做了一些分析/记录

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]);

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

因此,在主线程上自定义创建队列(其操作之间没有并发性)并不一定意味着操作将在主线程本身上串行执行。

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

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

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

您还可以通过使用 NSPrivateQueueConcurrencyType创建 NSManagedObjectContext并在其 performBlock:方法内执行请求,从而创建一个在自己的后台线程中执行请求的 NSManagedObjectContext

来自 NSOperationQueue

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

那么,

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

在您的示例中,您可能希望通过子类化 NSThread创建自己的“数据库线程”,并使用 performSelector:onThread:向其发送消息。

来自文档的摘要是 operations are always executed on a separate thread(后 iOS4意味着 GCD 底层操作队列)。

检查它是否确实在非主线程上运行是微不足道的:

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

当在一个线程中运行时,使用 GCD/lib 调度在主线程上运行一些东西是微不足道的,不管是核心数据、用户界面还是在主线程上运行所需的其他代码:

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

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

如果从头开始创建 NSOperationQueue,如下所示:

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

这将是在一个背景线程:

操作队列通常提供用于运行其 在 OS X v10.6及更高版本中,操作队列使用 Lib 调度库(也称为大中央调度)启动 因此,操作总是 在单独的线程 上执行,而不管它们是否 指定为并发或非并发操作

除非你正在使用 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操作参考,可以找到 给你。但简而言之,是为了避免保留周期。