32

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

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

問題は次のとおりです。

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

子 MOC (メイン スレッド上) が NSFetchedResultsController でフェッチを実行している場合、親コンテキストに -executeFetchRequest が送信されます。デッドロックが発生する可能性があります。両方の操作は、performBlock: のコンテキスト内でそれぞれの MOC に対して行われますが、ドキュメントは、メイン スレッド上で 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% 確信しています。

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

4

7 に答える 7

35

Fabrice truillot de chambrier が現在ネストされたコンテキストを使用しないことを推奨しているこの SO 投稿を読んだところです。彼は記事Core Data Growing Pains を参照しています。

その記事から:

NSFetchedResultsController のデッドロック

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

レーダー://11861499 は今後のリリースで修正されます

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

于 2012-08-10T11:36:54.313 に答える
2

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

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

最初に、savePrivateThreadMOCToCoreDataが -performBlock で保存を行っていたとき、上記の「コア データの成長の痛み」で説明されているのと同じ psynch_mutex デッドロックが発生していました。また、保存中に TableVC にデータを読み込もうとすると、クラッシュが発生しました。

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

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

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

私のビルド環境は iOS 6 ですが、私のベース デプロイメント ターゲット SDK iOS 5.0 はおそらく関連しています。他の人は、iOS 6 でこの問題をもう見ていないようです。

于 2013-02-03T03:14:43.043 に答える
0

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

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

于 2013-02-08T06:16:48.530 に答える
0

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

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

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

A(CDManager.privateContext) は B() を呼び出します B() は C() を呼び出します C() は D() を呼び出します

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

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

A (コンテキスト) 呼び出し B (コンテキスト) B (コンテキスト) 呼び出し C (コンテキスト) C (コンテキスト) 呼び出し D()

于 2015-01-07T07:41:55.973 に答える
0

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

Magical Record 3 には優れたソリューションが実装されています

于 2015-02-09T19:29:59.763 に答える
-1

長いハードワークと保存のほとんどは、プライベート MOC で行うことができます。

そのアイデアをどのように実装しますか?次のようなものを使用しますか?

- (void)doSomethingWithDocument:(UIManagedDocument *)document
{
    NSManagedObjectContext *parent = document.managedObjectContext.parentContext;
        [parent performBlock:^{
            /* 
               Long and expensive tasks.. 
               execute fetch request on parent context
               download from remote server               
            */
            // save document
        }];
}

私は上記を行い、デッドロックも発生しました。次に、親コンテキストのバッキング キューには触れないようにしました。代わりに、プレーンでシンプルな GCD を使用してダウンロードを行い、子コンテキスト (メイン キュー) でコア データを操作します。それは正常に動作します。このように、親コンテキストは役に立たないように見えます..しかし、少なくともデッドロックは発生しません..

- (void)doSomethingWithDocument:(UIManagedDocument *)document
{
    dispatch_queue_t fetchQ = dispatch_queue_create("Flickr fetcher", NULL);
    dispatch_async(fetchQ, ^{
        // download from remote server        
        // perform in the NSMOC's safe thread (main thread)
        [document.managedObjectContext performBlock:^{ 
            // execute fetch request on parent context
            // save document  
        }];
    });
    dispatch_release(fetchQ);
}
于 2012-10-06T08:58:34.037 に答える