web-dev-qa-db-ja.com

NSOperationおよびNSOperationQueueの作業スレッドとメインスレッド

アプリで一連のダウンロード操作とデータベース書き込み操作を実行する必要があります。同じためにNSOperationNSOperationQueueを使用しています。

これはアプリケーションシナリオです。

  • 場所からすべての郵便番号を取得します。
  • 郵便番号ごとにすべての家を取得します。
  • 各家の住民の詳細について

前述のように、各タスクに対してNSOperationを定義しました。最初のケース(Task1)では、すべての郵便番号を取得するリクエストをサーバーに送信しています。 NSOperation内のデリゲートはデータを受け取ります。その後、このデータはデータベースに書き込まれます。データベース操作は別のクラスで定義されています。 NSOperationクラスから、データベースクラスで定義された書き込み関数を呼び出しています。

私の質問は、データベースの書き込み操作がメインスレッドで発生するか、バックグラウンドスレッドで発生するかです。 NSOperation内で呼び出していたので、NSOperationとは異なるスレッド(MainThreadではない)で実行されることを期待していました。誰かがNSOperationNSOperationQueueを扱っている間にこのシナリオを説明してもらえますか。

80
Zach

私の質問は、データベースの書き込み操作がメインスレッドで発生するか、バックグラウンドスレッドで発生するかです。

次のようにゼロから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は、あなたがやりたいことをきめ細かく制御します。 2つの操作の間に依存関係を作成できます(ダウンロードしてデータベースに保存します)。あるブロックと他のブロックの間でデータを渡すために、たとえば、サーバーから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参照を使用している理由は、 こちら にあります。しかし、簡単に言えば、保持サイクルを回避することです。

173
Rui Peres

バックグラウンドスレッドでデータベース書き込み操作を実行する場合は、そのスレッドのNSManagedObjectContextを作成する必要があります。

関連するNSManagedObjectContextサブクラスのstartメソッドで背景NSOperationを作成できます。

Appleのドキュメントで、 コアデータとの同時実行性を確認してください。

NSManagedObjectContextでリクエストを作成し、performBlock:メソッド内でリクエストを実行することにより、独自のバックグラウンドスレッドでリクエストを実行するNSPrivateQueueConcurrencyTypeを作成することもできます。

16
serrrgi

から 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

あなたの場合、NSThreadをサブクラス化して独自の「データベーススレッド」を作成し、performSelector:onThread:を使用してメッセージを送信することができます。

11
ilya n.

NSOperationの実行スレッドは、操作を追加したNSOperationQueueに依存します。コードでこのステートメントを見てください-

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

これはすべて、mainNSOperationメソッドでこれ以上スレッドを実行していないことを前提としています。

ただし、同時操作の場合、シナリオは異なります。キューは、同時操作ごとにスレッドを生成する場合があります。保証されていませんが、システムリソースと操作リソースの要求に依存しますその時点でシステム内。 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]);

メインスレッドとしてではありませんでした。そして、現在のスレッドオブジェクトがメインスレッドオブジェクトではないことがわかりました。

したがって、メインスレッドでのキューのカスタム作成(操作間の同時実行性なし)は、操作がメインスレッド自体でシリアルに実行されることを必ずしも意味しません。

9
Ashok

ドキュメントの要約は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
});
1
duncanwilcox