1

要約:[myMOC mergeChangesFromContextDidSaveNotification:notification]マルチスレッド シナリオで呼び出すと、アプリがハングします。

詳細な状況は次のとおりです。

私のアプリはサーバーから大量のデータをダウンロードし、最初の起動時に Core Data に保存します。それはいくつかの部分に分かれています。全体を解析するには数秒かかり、その時間のほとんどは 2 つのチャンクに費やされます。高速化するために、これら 2 つのチャンクを並列化しました。これは次のようになります。

NSArray *arr = [self parseJsonData:downloadedNSData]; //turns NSData into JSON array
                     //using NSJSONSerialization
NSMutableArray __block *first = [[NSMutableArray alloc]init];
NSMutableArray __block *second = [[NSMutableArray alloc]init];

//put half of arr in first and half in second with a loop

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dipatch_group_create(); 
dispatch_group_async(group, queue, ^
   {
      for (id element in first)
      {
         [MyDataClass parseData:element]; //create NSManagedObject subclass, save
      }
      [self saveContext];
   });

    dispatch_group_async(group, queue, ^
   {
      for (id element in second)
      {
         [MyDataClass parseData:element]; //create NSManagedObject subclass, save
      }
      [self saveContext];
   });

  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

[self saveContext]saveMOC を呼び出します。これはもちろん複数のスレッドで行われるManagedObjectContextため、スレッドごとに個別に作成する必要があります。これは、s へのスレッド名 ( の呼び出し) を維持するこのオブジェクト ( ) のプロパティを介して MOC を公開することによって実現されselfます。アクセス時に の MOC がない場合は、新しい MOC を作成し、辞書に追加して保存します。各 MOC が作成されると、その変更通知にサブスクライブします。NSMutableDictionarydescriptionNSThreadNSManagedObjectContext[NSThread currentThread]

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(mergeChanges:)
      name:NSManagedObjectContextDidSaveNotification object:createdMOC];

ではmergeChanges、コンテキストの辞書をループして、mergeChangesFromContextDidSaveNotificationこれが発生しているスレッドの 1 つを除くすべてのコンテキストを呼び出します。より具体的には、[performSelector:onThread:withObject:waitUntilDone:]各 MOC が作成されたスレッドでこれを実行するように使用します。

と私の方法NSLockの両方を使用してロックしています。それらをロックしていなかった場合、「Cocoa error 133020」が表示れました。これは、変更のマージ エラーを意味しているようです。[myMOC save]mergeChanges

だから、これは私のロギングが私に言っていることです:

  • スレッド 1 は保存するためのロックを取得し、保存を開始します
  • スレッド 1 でマージ コンテキスト通知が送信されます。スレッド 1 は、変更をマージするためのロックを取得し、そのプロセスを開始します。メイン スレッドの MOC の変更をマージし、バックグラウンド スレッドの MOC の 1 つを実行するとハングします。
  • スレッド 2 は保存を開始しますが、保存のためのロックを取得しません。これは、他のスレッドが変更をマージしようとしてスタックしているためです。

では、変更をマージするとハングするのはなぜですか? このシナリオを処理するより良い方法はありますか?

更新:を使用してロックするだけでなく[persistentStoreCoordinator lock]、通話の周りで使用しようとしましたが、役に立ちませんでした。また、呼び出しの 1 つに呼び出しの前に追加しようとしましたが、役に立ちませんでした。[MOC save]NSLock[NSThread sleepForTimeInterval:2][self saveContext]dispatch_group_async

更新 2:おそらくここでのより良い質問は、マージの競合が発生した理由です (Cocoa エラー 133020)。それは期待されていますか?マージを正しく行っていますか (1 つの保存を除くすべてのコンテキストにマージしています)?

更新 3:マルチスレッドの実行方法に関するより大きなコンテキストに対処するために、別の質問を投稿しました。

4

2 に答える 2

0

NSManagedObjectサブクラスを作成するときは、それをコンテキストに挿入するため、コンテキストに属するキュー/スレッドでのみサブクラスを作成できます。

-saveコンテキストの呼び出しについても同じことが言えます。

あなたのコードから、2つのオブジェクトを挿入しているように見えます—それぞれが独自のスレッドにあります。それは機能しません。

于 2012-09-14T15:07:07.300 に答える
-1

この質問を投稿して、私の状況を少し改善しました。この現在の質問は、範囲が少し狭いと思います。

于 2012-09-26T16:24:26.117 に答える