私はココア ソフトウェアに取り組んでおり、大量のデータ インポート (コア データ) 中に GUI の応答性を維持するために、メイン スレッドの外でインポートを実行する必要があります。
スレッドの実行中にそれらのオブジェクトに明示的にアクセスしない場合、ロックを使用せずにメイン スレッドでそれらのオブジェクトを作成したとしても、それらのオブジェクトにアクセスしても安全ですか。
私はココア ソフトウェアに取り組んでおり、大量のデータ インポート (コア データ) 中に GUI の応答性を維持するために、メイン スレッドの外でインポートを実行する必要があります。
スレッドの実行中にそれらのオブジェクトに明示的にアクセスしない場合、ロックを使用せずにメイン スレッドでそれらのオブジェクトを作成したとしても、それらのオブジェクトにアクセスしても安全ですか。
Core Data では、同じコーディネーターと永続ストアに接続された、インポート スレッドに使用する別の管理対象オブジェクト コンテキストが必要です。メインスレッドによって使用されるコンテキストで作成されたオブジェクトを別のスレッドに単純にスローして、それらが機能することを期待することはできません。さらに、これに対して独自のロックを行うことはできません。必要に応じて、少なくともオブジェクトが含まれる管理対象オブジェクト コンテキストをロックする必要があります。ただし、これらのオブジェクトがビュー コントロールによってバインドされている場合、コンテキストのロックを追加できる「フック」はありません。
フリーランチはありません。
Ben Trumbull は、webobjects-dev リストの 2004 年後半に投稿されたこのすばらしい投稿で、別のコンテキストを使用する必要がある理由と、「ただ読む」ことが思っているほど単純でも安全でもない理由のいくつかを説明しています。(スレッド全体が素晴らしいです。) 彼は Enterprise Objects Framework と WebObjects について議論していますが、彼のアドバイスは Core Data にも完全に当てはまります。彼のメッセージの内容で、「EC」を「NSManagedObjectContext」に、「EOF」を「Core Data」に置き換えるだけです。
以前の Enterprise Objects Framework のように、Core Data のスレッド間でデータを共有する問題の解決策は、「しない」ことです。さらに考えてみて、実際にスレッド間でデータを共有する必要がある場合、解決策は、独立したオブジェクト グラフをスレッド分離コンテキストに保持し、1 つのコンテキストからの保存通知の情報を使用して、再フェッチする他のコンテキスト。 -[NSManagedObjectContext refreshObject:mergeChanges:]
は、この使用をサポートするように特別に設計されています。
これは、CoreData NSManagedObjectContextによって管理されるNSManagedObjects(またはサブクラス)で行うのは安全ではないと思います。一般に、CoreDataは、管理対象オブジェクトの状態に対して多くのトリッキーなことを行う可能性があります。これには、別々のスレッドでそれらのオブジェクトに関連する障害の発生が含まれます。特に[NSManagedObject initWithEntity:insertIntoManagedObjectContext:]
(OS X 10.5以降のNSManagedObjectsの指定された初期化子)は、返されたオブジェクトが他のスレッドに安全に渡されることを保証しません。
複数のスレッドでCoreDataを使用することは、Appleの開発サイトで十分に文書化されています。
ロックを使用することの要点は、2 つのスレッドが同じリソースにアクセスしようとしないようにすることです。他のメカニズムを通じてそれを保証できる場合は、それを選択してください。
安全であっても、それらのフィールドへのアクセスを同期せずにスレッド間で共有データを使用することはベスト プラクティスではありません。どのスレッドがオブジェクトを作成したかは問題ではありませんが、複数の実行行 (スレッド/プロセス) が同時にオブジェクトにアクセスしている場合は、データの不整合が発生する可能性があるためです。
1 つのスレッドだけがこのオブジェクトにアクセスすることが絶対に確実な場合は、アクセスを同期しない方が安全です。それでも、アプリケーションの変更により、アクセスの同期を気にせずに同じデータを共有する 2 番目のスレッドが配置されるまで待つよりも、今すぐコードに同期を組み込むことをお勧めします。
考慮すべき点は次の 2 つです。
はい、安全です。非常に一般的なパターンは、オブジェクトを作成し、それをキューまたは他のコレクションに追加することです。2 番目の「コンシューマー」スレッドは、キューから項目を取得し、それらに対して何かを行います。ここでは、キューを同期する必要がありますが、キューに追加されるオブジェクトを同期する必要はありません。
すべてを同期させて最善を尽くすのは得策ではありません。設計について非常に慎重に検討し、どのスレッドがオブジェクトに作用するかを正確に考える必要があります。
はい、できます。安全です。
... 2 番目のプログラマーがやって来て、あなたが行ったのと同じ仮定を理解しないまで。その 2 番目 (または 3 番目、4 番目、5 番目、...) のプログラマーは、(作成者スレッドで) 安全でない方法でオブジェクトを使用し始める可能性があります。発生する問題は非常に微妙で、追跡が困難な場合があります。その理由だけで、またこのオブジェクトを複数のスレッドで使用したくなるので、オブジェクトをスレッド セーフにします。
明確にするために、(コメントを残した人に感謝します):
「スレッドセーフ」とは、スレッド化の問題を回避するためのスキームをプログラムで考案することを意味します。オブジェクトの周りにロック方式を考案する必要はありません。作成者スレッドでオブジェクトを使用することを違法 (または非常に困難) にする方法を言語で見つけることができます。たとえば、クリエーター スレッドで、オブジェクトを作成するコードのブロックにスコープを制限します。作成したら、オブジェクトをユーザー スレッドに渡し、作成者スレッドがそれを参照していないことを確認します。
たとえば、C++ では
void CreateObject()
{
Object* sharedObj = new Object();
PassObjectToUsingThread( sharedObj); // this function would be system dependent
}
次に、作成スレッドでは、作成後にオブジェクトにアクセスできなくなり、責任が使用スレッドに渡されます。