NSOperation和NSOperationQueue工作线程vs主线程

Zac*_*ach 80 multithreading objective-c nsoperation nsoperationqueue ios

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

这是应用场景:

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

如上所述,我NSOperation为每项任务定义了一个.在第一种情况下(Task1),我向服务器发送请求以获取所有邮政编码.将在该会议中的代表NSOperation接收数据.然后将该数据写入数据库.数据库操作在不同的类中定义.从NSOperation类I开始调用数据库类中定义的write函数.

我的问题是数据库写操作是在主线程还是后台线程中发生的?正如我在其中调用它一样, NSOperation我期望它在不同的线程(Not MainThread)中运行NSOperation.有人可以在处理NSOperation和解释这个场景时解释NSOperationQueue.

Rui*_*res 174

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

如果您NSOperationQueue从头开始创建:

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
Run Code Online (Sandbox Code Playgroud)

它将在后台线程中:

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

除非你使用mainQueue:

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
Run Code Online (Sandbox Code Playgroud)

你还可以看到这样的代码:

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{

   // Background work

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // Main thread work (UI usually)
    }];
}];
Run Code Online (Sandbox Code Playgroud)

和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)                          
              });
});
Run Code Online (Sandbox Code Playgroud)

NSOperationQueue更好地控制你想做的事情.您可以在两个操作之间创建依赖关系(下载并保存到数据库).要在一个块和另一个块之间传递数据,您可以假设,例如,a 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];
Run Code Online (Sandbox Code Playgroud)

编辑:为什么我使用__weak操作的参考,可以在这里找到.但简而言之就是避免保留周期.

  • 答案是正确的但请注意正确的代码行是:`NSOperationQueue*mainQueue = [NSOperationQueue mainQueue];`并且主线程上的NSOperationQueue不能被挂起. (3认同)
  • @ jacky-boy出于兴趣,为什么在最终的代码片段中使用弱引用的downloadOperation和saveToDataBaseOperation? (2认同)

ser*_*rgi 16

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

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

检查Apple文档中的核心数据并发性.

您还可以NSManagedObjectContext通过NSPrivateQueueConcurrencyType在其performBlock:方法内创建请求并在其方法中执行请求来创建在其自己的后台线程中执行请求的请求.

  • 是什么让你认为这个问题与核心数据有关? (15认同)

ily*_* n. 11

来自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
Run Code Online (Sandbox Code Playgroud)

在您的情况下,您可能希望通过子类化NSThread并使用发送消息来创建自己的"数据库线程" performSelector:onThread:.


Ash*_*hok 9

NSOperation的执行线程取决于NSOperationQueue您添加操作的位置.在你的代码中查看此语句 -

[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class
Run Code Online (Sandbox Code Playgroud)

所有这些假设你没有做任何进一步的线程,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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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