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


81

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

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

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

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

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


3
メインキューに操作を追加すると、それらはメインスレッドで実行されます。独自のNSOperationQueueを作成してそれに操作を追加すると、それらはこのキューのスレッドで実行されます。
Cy-4AH 2013年

1
より具体的に/コードを投稿しない限り、@ Cy-4AHが提供したよりも良い答えが得られるとは思いません。私はあなたが常にコードにブレークポイントを置くことができると言うだろうし、それがトリップするとき、それはである何トレーススレッドを紹介します。
ブラッド・オールレッドを

「NSOperation内のデリゲートはデータを受信します」とは何ですか。平均?デリゲートプロパティNSOperationNSOperationQueue含まれていません。
ジェフリートーマス

現在のスレッドについて推測するのではなく、デリゲート呼び出しをメインスレッドにプッシュすることもできます...
Wain 2013年

回答:


175

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

次のように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つの操作(ダウンロードしてデータベースに保存)の間に依存関係を作成できます。あるブロックと別のブロックの間でデータを渡すために、たとえば、aNSDataがサーバーから来ると想定できます。

__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オペレーションのリファレンスを使用しているのか、ここで見つけることができます。しかし、一言で言えば、保持サイクルを回避することです。


3
答えは正しいですが、正しいコード行は次NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];のとおりです。メインスレッドのNSOperationQueueを一時停止することはできません。
Gianluca P.

2
@ jacky-boy興味深いことに、最終的なコードスニペットでdownloadOperationとsaveToDataBaseOperationへの弱参照を使用するのはなぜですか?
マイケルウォーターフォール2014年

RuiAAPeres、ねえ、最終的なコードスニペットで弱い参照が使用されているのはなぜですか?それに光を当てていただけませんか?
パバン2014年

@Pavanの考え方は、同じブロック操作をそれ自体のブロック内で参照すると、保持サイクルが発生するというものです。参考:conradstoll.com/blog/2013/1/19
Rui Peres

では、どのブロックがその内部で実行されているブロックと同じですか?
パバン2014年

16

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

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

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

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


17
この質問がコアデータに関連していると思う理由は何ですか?
Nikolai Ruhe 2013年

11

NSOperationQueueから

iOS 4以降では、操作キューはGrand CentralDispatchを使用して操作を実行します。iOS 4より前は、非並行操作用に個別のスレッドを作成し、現在のスレッドから並行操作を起動します。

そう、

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

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


どうもありがとう... u私の命を救う:[NSOperationQueue new] // iOS4以降、メインスレッドではないことが保証されている
ikanimo 2014年

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

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

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


を使用[[NSOperationQueue alloc] init]すると、キューが基になるデフォルトのバックグラウンドグローバルDispatchQueueを使用することが保証されると思います。したがって、カスタムで初期化されたOperationQueueは、そのタスクをバックグラウンドのDispatchQueuesにフィードするため、メインスレッドではないスレッドにタスクをフィードします。操作をメインスレッドにフィードするには、のようなものを使用してメインのOperationQueueを取得する必要があります[NSOperationQueue mainQueue]。これにより、メインのDispatchQueueを内部で使用するメインの操作キューが取得されるため、最終的にはタスクがメインスレッドにフィードされます。
ゴルドニウム2018

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

-2

重要なスレッドを実行している場合は、FMDatabaseQueueを使用する必要があります。


1
この質問では、fmdbやsqliteなどの特定のデータベースについては触れられていません。
Nikolai Ruhe 2013年

それは正しいです、私は文脈に基づいて仮定をしました。アプリがMySQLなどのマルチテナントのスレッドセーフデータベースに接続している場合、この質問は意味がありません。使用できるシングルユーザーデータベースは他にもありますが、SQLiteが最も一般的です。
ホリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.