2

バックグラウンドスレッドが所有する新しく作成されたNSManagedObjectContextを介して、バックグラウンドスレッドで(GCDを使用して)高価なフェッチ(約5秒、約30,000オブジェクト)を実行しています。

ただし、メインスレッドが永続ストアのロックを待機しているため、UIがフリーズするため、これをバックグラウンドで実行するメリットは得られません。スタックトレースは次のとおりです。

* thread #1: tid = 0x1c03, 0x3641be78 CoreData`-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore], stop reason = breakpoint 1.1
    frame #0: 0x3641be78 CoreData`-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore]
    frame #1: 0x36432f06 CoreData`-[_PFManagedObjectReferenceQueue _processReferenceQueue:] + 1754
    frame #2: 0x36435fd6 CoreData`_performRunLoopAction + 202
    frame #3: 0x35ab9b1a CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 18
    frame #4: 0x35ab7d56 CoreFoundation`__CFRunLoopDoObservers + 258
    frame #5: 0x35ab80b0 CoreFoundation`__CFRunLoopRun + 760
    frame #6: 0x35a3b4a4 CoreFoundation`CFRunLoopRunSpecific + 300
    frame #7: 0x35a3b36c CoreFoundation`CFRunLoopRunInMode + 104
    frame #8: 0x376d7438 GraphicsServices`GSEventRunModal + 136
    frame #9: 0x33547cd4 UIKit`UIApplicationMain + 1080
    frame #10: 0x000f337a MyApp`main + 90 at main.m:16

私は(信じています)このバックグラウンドスレッドが作業を行っている間、メインスレッドのNSManagedObjectContextにアクセスしていないことを確認しました。そして、スタックトレースから、私がCoreDataで直接何もしていないことは明らかです。しかし、何かが_processReferenceQueueの呼び出しをトリガーしました。これにより、ストアをロックしようとします。誰かがこのメソッドが何をするのか、そして私のバックグラウンドスレッドがその仕事をしている間にそれが呼び出されるのをどのように/防ぐことができるかを知っていますか?

編集

このバックグラウンドフェッチを開始した後、メインスレッドでCoreDataの読み取りまたは書き込みを行っていません。だからこれはとても不可解です。メインスレッドもさらに作業を行おうとすると、競合が発生することが予想されますが、そうではありません。少なくとも、私はそれを要求していません。読み取り、書き込み、FRCはありません。そのため、この_processReferenceQueueメソッドに精通している人がいるかどうかを知りたいと思います。なぜ呼ばれるのですか?何が原因で実行されているのでしょうか。

編集

テストとして、MTのMOCを保留中の変更がない状態にしてから、BTをオフにしてフェッチを実行しようとしました。_processReferenceQueueこれは、ロックが必要な作業を行う必要がないことを期待しています。店で。

BTを開始する前に、[MOC updatedObjects]セットに1つのオブジェクトがあることに気づきました。挿入または削除されたセットにオブジェクトはありませんでした。

を呼び出した後、予想どおり[MOC save][MOC updatedObjects]セットは空でした。しかし、私がBTを開始した後も、MOCに汚れがないはずなのに
、MTはまだ店内をロックしようとしました。_processReferenceQueue

私が(厳密にテストとして)次に試したMOC reset]のは、BTを開始する前に代わりに[を呼び出すことでした。[MOC updatedObjects]予想通り、リセット後もセットは空でした。コードのこの時点では、BTが作業を終了するまで、MT上の管理対象オブジェクトに触れていません(したがって、既に参照している管理対象オブジェクトを無効にするリセットが原因で問題が発生することはありません)。ただし、今回は、MTはで永続ストアをロックしようとしません_processReferenceQueueでした。

この動作は、BTを開始した後、MTのMOCで明示的なことを何もしていないことを示しています。そうしないと、MTは、の内部または外部のある時点(読み取りまたは書き込み用)でロックを要求することになります_processReferenceQueue。しかし、そうではありませんでした。

最近保存されたMOCが後続のロックインを必要とするの_processReferenceQueueに対し、最近リセットされたMOCが必要としない理由はわかりません。

掘り続けます。

ありがとう!ブライアン

4

1 に答える 1

2

これ以上の情報がなければ、私にできることは推測することだけです。

残念ながら、Core Data は、同じ永続ストア コーディネーターを使用する MOC のリーダー/ライター ロックを実装していません。つまり、1 つのスレッドで読み取りを行うと、同じ永続ストア コーディネーターを使用する他のスレッドがブロックされます。

したがって、バックグラウンドで長時間フェッチを実行すると、メイン スレッドがフェッチできなくなります。

いくつかのオプションがあります。

長時間実行されるフェッチを分割して、小さい断片を順番にフェッチするようにします。現在のピースが完了するまで次のピースを発行せずに、複数の小さなフェッチを実行したい。

これにより、メイン スレッドでのフェッチが、バックグラウンド スレッドでの複数の小さなフェッチの間に処理される機会を得ることができます。

もう 1 つの方法は、個別の永続ストア コーディネーターを作成し、バックグラウンド MOC をそれに接続して、フェッチを実行することです。複数の永続ストア コーディネーターを使用する場合、複数のリーダーを同時に持つことができ、相互に永続的にブロックされることはありません。

編集

私は間違いなくそれを壊すことを考えました。しかし、これは、2 つの同時フェッチをうまく連携させようとしている場合ではありません。この時点で、MT は Core Data に触れてはいけません。– ブライアン・スタッフォード

を使用しUIManagedDocumentていますか、またはメイン スレッドまたは で MOC を作成しましたNSMainQueueConcurrencyTypeか?

Core Data は、実行ループのたびにすべての「ユーザー イベント」が処理されるようにするためです。これがどのように行われるかを正確に判断したことはありませんが、メインスレッドにあるMOCの実行ループにハンドラーを追加すると思います。

Core Data は、複数の MOC がある場合を除いて、非常に簡単に使用できます。次に、相互作用が非常に複雑になるため、実際のコードがないと何が起こっているのかを判断することが困難になります。

可能であれば、任意/すべての MOC を作成するコードと、それらの MOC を使用するコードを投稿してください。

現在のコンテキストとは何の関係もない場合、run-loop-processing が実際にストアのロックを取得することを心から疑っています。そのため、バックグラウンド MOC との隠れた対話が行われている可能性があります。私の賭けは、まだあなたがしているとは思わないあなたのアプリの何かであり、何らかの形でバックグラウンド作業をメインの MOC と交差させています。

[_PFManagedObjectReferenceQueue _processReferenceQueue:]必要に応じて、 がどのように呼び出されたかを簡単に追跡できます。

テスト 1. 簡単なプロジェクトを作成し、Core Data ボックスをオンにして、簡単な Core Data テンプレート プロジェクトを取得します。ブレークポイント_processReferenceQueue:を確実に取得できるように、ブレークポイントを配置します。

テスト 2.コア データをインスタンス化する部分をコメント アウトし、フレームワークにリンクするだけでブレークポイントに到達するかどうかを確認します。

テスト 3. MOC を作成しますが、他には何もしません。作成するだけです。ブレークポイントに到達するかどうかを確認するために繰り返します。

テスト 4.「デフォルト MOC」を削除して、基本的にテスト 2 と同じようにします。次に、プライベート キュー MOC を作成し、それを介して簡単なトランザクションを実行performBlockし、ブレークポイントに到達するかどうかを確認します。

これがどこに向かっているのかがわかります。基本的に、非常に単純なユース ケースのどの組み合わせがメイン スレッドのブレークポイントに到達するかを判断します。

これにより、少なくとも、フレームワーク自体がこれを引き起こしているかどうか、いつ発生したかがわかります。これにより、複雑なアプリが何をしているかがわかります。

編集

好奇心が勝った。両方にブレークポイントを設定して、いくつかの簡単なテストを実行しました。

-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore]
-[_PFManagedObjectReferenceQueue _processReferenceQueue:]

実際にMOCをいじらない限り、どちらもヒットしていません。各 MOC は閉じ込め同時実行タイプです。100,000 個の単純なエンティティを含むデータベースを作成しました。

メインスレッドは FRC を使用してデータをロードします。dispatch_async押すと a が起動し、 100,000 個のオブジェクトすべてをフェッチするボタンを追加しました。

補助スレッドでは両方のブレークポイントがヒットしていますが、どちらもメイン スレッドではヒットしていません。もちろん、メイン スレッドで MOC をフェッチまたは更新すると、これらのブレークポイントにヒットします。

私はシミュレーターで実行しています。

したがって、私の結論は、CoreData 自体は、表示されている動作に責任を負わないということです。

おそらく、MOC、永続ストア コーディネーター、永続ストアの構成、またはコンテキスト間の未知の相互作用が原因である可能性があります。

オブジェクトと実際のコードの構成に関する詳細な情報がなければ、私の最良の結論は、これを発生させるためにコードで「何か」を行っているということです。

これらのブレークポイントを設定し、ヒットしたときにコードで何が起こっているかを確認する必要があります。

于 2012-08-29T02:50:17.553 に答える