我必须在我的应用程序中执行一系列下载和数据库写入操作。我正在使用NSOperation
和NSOperationQueue
。
这是应用场景:
如上所述,我为每项任务定义了NSOperation
。在第一种情况下(Task1),我向服务器发送请求以获取所有邮政编码。 NSOperation
内的代表将收到数据。然后将该数据写入数据库。数据库操作在不同的类中定义。从NSOperation
类开始,我调用了数据库类中定义的write函数。
我的问题是数据库写操作是在主线程还是后台线程中发生?当我在NSOperation
内调用它时,我期望它在NSOperation
的不同线程(Not MainThread)中运行。有人可以在处理NSOperation
和NSOperationQueue
时解释这种情况。
答案 0 :(得分:171)
我的问题是数据库写操作是否发生在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
可以更好地控制您想要做的事情。您可以在两个操作之间创建依赖关系(下载并保存到数据库)。要在一个块和另一个块之间传递数据,您可以假设,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
操作参考,可以找到here。但简而言之就是避免保留周期。
答案 1 :(得分:16)
如果要在后台线程中执行数据库写入操作,则需要为该线程创建NSManagedObjectContext
。
您可以在相关NSManagedObjectContext
子类的start方法中创建背景NSOperation
。
查看Apple文档中的Concurrency with Core Data.
您还可以创建一个NSManagedObjectContext
,通过NSPrivateQueueConcurrencyType
创建请求并在其performBlock:
方法中执行请求,在自己的后台线程中执行请求。
答案 2 :(得分:9)
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]);
它不是主线程。并且,发现当前线程对象不是主线程对象。
因此,在主线程上自定义创建队列(在其操作中没有并发)并不一定意味着操作将在主线程本身上串行执行。
答案 3 :(得分:9)
在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:
向其发送消息。
答案 4 :(得分:1)
文档摘要为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
});
答案 5 :(得分:-2)
如果您正在进行任何非平凡的线程,则应使用FMDatabaseQueue。