この答えを参考にして、これは正しいのだろうか?
@synchronizedはコードを「スレッドセーフ」にしません
このステートメントをサポートするドキュメントまたはリンクを見つけようとしましたが、成功しませんでした。
コメントおよび/または回答はこれについて高く評価されます。
スレッドセーフを向上させるために、他のツールを使用することができます。これは私には知られています。
この答えを参考にして、これは正しいのだろうか?
@synchronizedはコードを「スレッドセーフ」にしません
このステートメントをサポートするドキュメントまたはリンクを見つけようとしましたが、成功しませんでした。
コメントおよび/または回答はこれについて高く評価されます。
スレッドセーフを向上させるために、他のツールを使用することができます。これは私には知られています。
@synchronized
適切に使用すれば、コードをスレッドセーフにします。
例えば:
スレッドセーフでないデータベースにアクセスするクラスがあるとします。クラッシュが発生する可能性があるため、データベースの読み取りと書き込みを同時に行いたくありません。
だから私は2つの方法があるとしましょう。storeData: LocalStore と呼ばれるシングルトン クラスの readData。
- (void)storeData:(NSData *)data
{
[self writeDataToDisk:data];
}
- (NSData *)readData
{
return [self readDataFromDisk];
}
これらのメソッドをそれぞれ独自のスレッドにディスパッチするとしたら、次のようになります。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[LocalStore sharedStore] storeData:data];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[LocalStore sharedStore] readData];
});
クラッシュする可能性があります。ただし、使用する storeData および readData メソッドを変更すると、@synchronized
- (void)storeData:(NSData *)data
{
@synchronized(self) {
[self writeDataToDisk:data];
}
}
- (NSData *)readData
{
@synchronized(self) {
return [self readDataFromDisk];
}
}
これで、このコードはスレッド セーフになります。ただし、ステートメントの 1 つを削除すると@synchronized
、コードはスレッドセーフではなくなることに注意してください。または、 の代わりに別のオブジェクトを同期する場合self
。
@synchronized
同期しているオブジェクトにミューテックス ロックを作成します。つまり、コードがブロック内のコードにアクセスしたい場合、@synchronized(self) { }
同じブロック内で実行されている以前のすべてのコードの後ろに並ぶ必要があります。
異なる localStore オブジェクトを作成した場合、 は@synchronized(self)
各オブジェクトを個別にロックするだけです。それは理にかなっていますか?
このように考えてください。たくさんの人が別々の列に並んで待っています。各列には 1 ~ 10 の番号が付けられています。各人がどの列で待機するか (列ごとに同期することによって) を選択できます。または、使用しない場合は@synchronized
、前にまっすぐジャンプしてすべての列をスキップできます。1 列目にいる人は 2 列目にいる人が終わるのを待つ必要はありませんが、1 列目にいる人は自分の列の前にいる全員が終わるのを待つ必要があります。
質問の本質は次のとおりだと思います。
同期を適切に使用すると、スレッドセーフの問題を解決できますか?
技術的には可能ですが、実際には他のツールを学習して使用することをお勧めします。
予備知識なしでお答えします。
正しいコードとは、その仕様に準拠したコードです。優れた仕様は定義します
スレッド セーフ コードは、複数のスレッドで実行された場合でも正しいままのコードです。したがって、
高レベルの要点は次のとおりです。スレッドセーフでは、マルチスレッドの実行中に仕様が true を保持する必要があります。これを実際にコーディングするには、変更可能な共有状態へのアクセスを規制するという 1 つのことだけを行う必要があります3。そして、それを行うには3つの方法があります:
最初の 2 つは単純です。3 つ目は、次のスレッド セーフの問題を防ぐ必要があります。
if (counter) counter--;
、いくつかの解決策の 1 つが@synchronize(self){ if (counter) counter--;}
です。これらの問題を解決するために@synchronize
、揮発性、メモリ バリア、アトミック操作、特定のロック、キュー、シンクロナイザー (セマフォ、バリア) などのツールを使用します。
そして質問に戻ります:
@synchronize を適切に使用すると、スレッドセーフな問題を解決できますか?
技術的には、上記のどのツールも でエミュレートできるためです@synchronize
。しかし、パフォーマンスが低下し、活性に関連する問題が発生する可能性が高くなります。代わりに、状況ごとに適切なツールを使用する必要があります。例:
counter++; // wrong, compound operation (fetch,++,set)
@synchronize(self){ counter++; } // correct but slow, thread contention
OSAtomicIncrement32(&count); // correct and fast, lockless atomic hw op
リンクされた質問の場合、実際に@synchronize
、または GCD 読み取り/書き込みロックを使用するか、ロック ストリッピングを使用してコレクションを作成するか、状況に応じて必要なものを何でも使用できます。正しい答えは、使用パターンによって異なります。どのような方法であれ、提供しているスレッドセーフな保証をクラスに文書化する必要があります。
1 つまり、オブジェクトが無効な状態にあるか、事前/事後条件に違反しています。
2 たとえば、スレッド A がコレクション X を反復し、スレッド B が要素を削除すると、実行がクラッシュします。これは、クライアントが X ( synchronize(X)
) の固有ロックで同期して排他的アクセスを行う必要があるため、スレッドセーフではありません。ただし、反復子がコレクションのコピーを返す場合、コレクションはスレッド セーフになります。
3 不変の共有状態、または可変の非共有オブジェクトは、常にスレッドセーフです。
@synchronized だけではコードをスレッド セーフにすることはできませんが、スレッド セーフ コードを記述する際に使用されるツールの 1 つです。
マルチスレッド プログラムでは、複雑な構造を一貫した状態に維持し、一度に 1 つのスレッドだけにアクセスさせたい場合がよくあります。一般的なパターンは、ミューテックスを使用して、構造体がアクセスまたは変更されるコードのクリティカル セクションを保護することです。
@synchronized
thread safe
メカニズムです。この関数内に記述されたコードはcritical section
、一度に 1 つのスレッドしか実行できない の一部になります。
@synchronize
ロックを暗黙的に適用するのに対しNSLock
、明示的に適用します。
スレッドの安全性を保証するだけで、それを保証するものではありません。私が言いたいのは、あなたの車のために専門のドライバーを雇うということですが、それでも車が事故に遭わないという保証はありません。ただし、確率はごくわずかです。
同行者GCD
(グランドセントラル発送)は着払いですdispatch_once
。dispatch_once は to と同じ働きをし@synchronized
ます。
この@synchronized
ディレクティブは、Objective-C コードでオンザフライでミューテックス ロックを作成する便利な方法です。
ミューテックス ロックの副作用:
@synchronized
スレッドの安全性は、ブロックの使用法に依存します。