web-dev-qa-db-ja.com

CoreDataのネストされた管理対象オブジェクトコンテキストと頻繁なデッドロック/フリーズ

この人がここで説明した問題とほぼ同じ問題がありますが、回答がありません:

http://www.cocoabuilder.com/archive/cocoa/312683-core-data-nested-managed-object-contexts-and-frequent-deadlocks.html#31268

ここに問題があります:

NSPrivateQueueConcurrencyTypeを使用した親MOCセットアップと、永続ストアコーディネーターセットがあり、NSMainQueueConcurrencyTypeを使用した子MOCセットアップがあります。長いハードワークと保存のほとんどであるという考えは、メインスレッドがUIをブロックすることから解放するプライベートMOCで実行できます。残念ながら、デッドロックを引き起こすいくつかの状況に遭遇しているようです。

子MOC(メインスレッド上)がNSFetchedResultsControllerを使用してフェッチを実行している場合、親コンテキストに-executeFetchRequestが送信されます。デッドロックが発生する可能性があります。両方の操作は、それぞれのMOCに対してperformBlock:のコンテキスト内で実行されますが、ドキュメントでは、performBlock:を使用せずにメインスレッドでメインスレッドの同時実行タイプMOCを使用することは問題ないと示されているようです。

プライベートキューは、メインスレッドの子コンテキストがすでにロックしているPSCロックを待機しているようです。子コンテキスト(PSCロックを保持している間)が親コンテキストにdispatch_syncを試行しているようであるため、両方が互いに待機しています。

PriveQueue-> MainQueueはサポートされている構成ですか?ほとんどの人はまだメインスレッドに親コンテキストを持っているようです。

メインスレッドは次のようになります。

> #0    0x960f6c5e in semaphore_wait_trap ()
> #1    0x04956bb5 in _dispatch_thread_semaphore_wait ()
> #2    0x04955c8f in _dispatch_barrier_sync_f_slow ()
> #3    0x04955dea in dispatch_barrier_sync_f ()
> #4    0x01797de5 in _perform ()
> #5    0x01798547 in -[NSManagedObjectContext(_NestedContextSupport) newValuesForObjectWithID:withContext:error:] ()
> #6    0x0176416b in _PFFaultHandlerLookupRow ()
> #7    0x01763f97 in -[NSFaultHandler fulfillFault:withContext:forIndex:] ()
> #8    0x01763b75 in _PF_FulfillDeferredFault ()
> #9    0x017639f2 in _sharedIMPL_pvfk_core ()
> #10    0x017681a0 in _pvfk_11 ()
> #11    0x0001b322 in -[FBUser sectionName] at /Users/mlink/Code/x/x/FBUser.m:62
> #12    0x011a8813 in _NSGetUsingKeyValueGetter ()
> #13    0x017a0652 in -[NSManagedObject valueForKey:] ()
> #14    0x011ab8d5 in -[NSObject(NSKeyValueCoding) valueForKeyPath:] ()
> #15    0x01851f72 in -[NSFetchedResultsController(PrivateMethods) _sectionNameForObject:] ()
> #16    0x01853af6 in -[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:] ()
> #17    0x01850ea6 in -[NSFetchedResultsController performFetch:] ()
> #18    0x0003a4fc in __62-[SYFriendsTableViewController updateFetchedResultsController]_block_invoke_0 ()
> #19    0x01797af3 in developerSubmittedBlockToNSManagedObjectContextPerform ()
> #20    0x049554f0 in _dispatch_main_queue_callback_4CF ()
> #21    0x01b3e833 in __CFRunLoopRun ()
> #22    0x01b3ddb4 in CFRunLoopRunSpecific ()
> #23    0x01b3dccb in CFRunLoopRunInMode ()
> #24    0x023d6879 in GSEventRunModal ()
> #25    0x023d693e in GSEventRun ()
> #26    0x0089aa9b in UIApplicationMain ()
> #27    0x00002656 in main at /Users/mlink/Code/x/x/main.mm:16

プライベートキュースタックは次のようになります。

#0    0x960f8876 in __psynch_mutexwait ()
#1    0x97e9e6af in pthread_mutex_lock ()
#2    0x0172ec22 in -[_PFLock lock] ()
#3    0x0172ebfa in -[NSPersistentStoreCoordinator lock] ()
#4    0x01746a8c in -[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore] ()
#5    0x01745030 in -[NSManagedObjectContext executeFetchRequest:error:] ()
#6    0x0009d49f in -[NSManagedObjectContext(Additions) executeFetchRequest:] at /Users/mlink/Code/objc/C/C/NSManagedObjectContext+Additions.m:44
#7    0x0002177f in +[FBUser usersForFbids:inManagedObjectContext:] at /Users/mlink/Code/x/x/FBUser.m:435
#8    0x00021fc0 in __77+[FBUser updateUserFromGraphValues:inManagedObjectContext:completionHandler:]_block_invoke_0 at /Users/mlink/Code/x/x/FBUser.m:461
#9    0x0180f9f3 in developerSubmittedBlockToNSManagedObjectContextPerform_privateasync ()
#10    0x04954ecf in _dispatch_queue_drain ()
#11    0x04954d28 in _dispatch_queue_invoke ()
#12    0x049544af in _dispatch_worker_thread2 ()
#13    0x97ea1b24 in _pthread_wqthread ()
#14    0x97ea36fe in start_wqthread ()

彼はまたこれを書いています:

問題は、これらのデッドロックが発生したときに常にperformFetchでスタックするNSFetchedResultsControllerにあると思い始めています。ほとんどの場合、セクション名を要求した結果として、オブジェクトに障害が発生しようとしてスタックします。テストとして、FRCの動作を再現し、executeFetchRequest:を実行してから、各オブジェクトにセクション名を尋ねる結果を繰り返しました。そして、これはデッドロックを引き起こしません。私がFRCを離れてperformFetchを実行した場合、テストを実行した後も、そこでデッドロックが発生します。 FRCにはネストされたコンテキストとの同期の問題があると99%確信しています。

質問:この問題が発生する理由を誰かが知っていますか?あなたはそれを解決する方法を知っていますか?これはバグですか?

31
thejaz

私はちょうど読んだ this SO posting ここでfabrice truillot de chambrierは現在ネストされたコンテキストを使用しないことを推奨しています。彼は記事への参照を与えます Core Data Growing痛み

その記事から:

NSFetchedResultsControllerのデッドロック

アプリケーションがデッドロックすることは決してありません。 NSFetchedResultsControllerとネストされたコンテキストを使用すると、非常に簡単に実行できます。上記と同じUIManagedDocumentセットアップを使用して、メインキューコンテキストでNSFe​​tchedResultsControllerを使用しているときに、プライベートキューコンテキストでフェッチ要求を実行すると、デッドロックが発生する可能性があります。両方をほぼ同時に開始すると、ほぼ100%の一貫性で発生します。 NSFetchedResultsControllerは、本来あるべきではないロックを取得している可能性があります。これは、iOSの今後のリリースで修正されたと報告されています。

レーダー:// 118614999は今後のリリースで修正されました

これはあなたの問題を正確に説明しているようです。

34
Martin R

IOS 6アプリの場合、OPと同じ同時実行設定があります。プライベートキューを使用する親MOCと、メインスレッドの子MOCです。また、子MOCを使用してUITableViewControllerを更新するNSFetchedResultsControllerもあります。これらのMOCは両方ともAppDelegateで初期化され、アプリ全体で使用されます。 AppDelegateには、変更を永続化するための2つのメソッドsavePrivateThreadMOCToCoreDatasaveMainThreadMOCToCoreDataがあります。 CD。起動時に、次のようにプライベートキューにcoredata初期化子をディスパッチします。アイデアは、ユーザーをすぐにテーブルビューにドロップし、イニシャライザーがバックグラウンドでコアデータを更新できるようにすることです。

    dispatch_async(private_queue,^{
        [CoreDataInitializer initialize];
    });

最初に、savePrivateThreadMOCToCoreDataが-performBlockで保存を行っていたときに、「 Core Data Growing Pains 」で説明されているのと同じpsynch_mutexデッドロックが発生していました。 = "上記にリンクされています。保存の進行中にTableVCにデータを読み取ろうとすると、クラッシュすることもありました。

    Collection <__NSCFSet: 0x7d8ea90> was mutated while being enumerated.

これらを克服するために、-performBlockAndWaitを使用して保存を行うように切り替えました。デッドロックとクラッシュが表示されなくなりましたが、UIに保存を待機させるのは適切ではありませんでした。最後に、-performBlock *へのすべての呼び出しを削除し、プレーンなVanilla [privateMOC save:&error]を使用しました。そのように、すべての問題が解消されました。フェッチされた結果コントローラーは、部分的に保存されたデータをクリーンに読み取り、テーブルを更新します。デッドロックや「列挙中に変更された」エラーは発生しません。

-performBlock*はotherスレッドによって使用されることになっていると思いますが、問題のMOCを作成しなかったもので、その操作を要求します。私のプライベートスレッドMOCとメインスレッドMOCはどちらもアプリデリゲートに属しているため、プライベートMOCでの保存では-performBlock*を使用しないでください。

私のビルド環境はiOS6ですが、基本展開のターゲットSDK iOS5.0はおそらく関連性があります。他の人はiOS6でこの問題をもう見ていません。

2

私もdeveloperSubmittedBlockToNSManagedObjectContextPerformに関連するクラッシュが発生しました。

私の場合、次のメソッド呼び出しパターンを検討してください。

[privatecontext performBlock:^{
    A(CDManager.privatecontext);
}];

ここで、A(CDManager.privateContext)呼び出しB() B()呼び出しC() C()呼び出しD()

および:メソッドA()およびメソッドC()には、いくつかのCoreData操作が含まれています。 A()は、どのコンテキストで作業するかをすでに知っていますが、A()はB()にコンテキストについて通知しないため、C()も持っていません。作業するコンテキストに関する情報。C()はデフォルトのコンテキスト(メイン)で機能します。これにより、db内のデータに一貫性がないためにクラッシュが発生します。

修正:db操作で機能する必要がないため、D()を除いて、db操作で機能するすべてのメソッドは、それらが機能するコンテキストでパラメーター化されます。

A(context) calls B(context) B(context) calls C(context) C(context) calls D()

0
Firdous

2つのスレッドからの同時フェッチによって引き起こされるデッドロックに関するまったく同じ問題を解決しました(BGはfired fetchRequestを実行し、MAINはNSFRCのフェッチを実行しました)。解決策は、長時間実行される同期操作の新しいコンテキストを作成することです。親コンテキストはなく、同時実行タイプはNSPrivateQueueConcurrencyTypeであり、共通のPSCに直接リンクされています。このコンテキスト内でバックグラウンドですべての長時間実行作業が行われた後、それを保存し、mergeChangesFromContextDidSaveNotificationルーチンを使用して残りの並列コンテキストスタックとマージします。

Magical Record 3には、優れたソリューションが実装されています。詳細については、こちらをご覧ください: https://stackoverflow.com/a/25060126/1885326

0
kas-kad

親がNSMainQueueConcurencyTypeで設定されているため、これが発生します

これを解決するために、mainQueueのmanagedobjectcontextを子にします。 mainQueueのデータが親と同じになるように、データをロードするたびにresetを呼び出しました。多くの場合、そうではありません。

0
user4951