33

この答えを参考にして、これは正しいのだろうか?

@synchronizedはコードを「スレッドセーフ」にしません

このステートメントをサポートするドキュメントまたはリンクを見つけようとしましたが、成功しませんでした。

コメントおよび/または回答はこれについて高く評価されます。

スレッドセーフを向上させるために、他のツールを使用することができます。これは私には知られています。

4

6 に答える 6

41

@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 列目にいる人は自分の列の前にいる全員が終わるのを待つ必要があります。

于 2013-03-13T18:10:17.713 に答える
22

質問の本質は次のとおりだと思います。

同期を適切に使用すると、スレッドセーフの問題を解決できますか?

技術的には可能ですが、実際には他のツールを学習して使用することをお勧めします。


予備知識なしでお答えします。

正しいコードとは、その仕様に準拠したコードです。優れた仕様は定義します

  • 状態を制約する不変式、
  • 操作の効果を説明する事前条件と事後条件。

スレッド セーフ コードは、複数のスレッドで実行された場合でも正しいままのコードです。したがって、

  • 操作の順序が仕様に違反することはありません。1
  • クライアントによる追加の同期を必要とせずに、マルチスレッドの実行中に不変条件と条件が保持されます2

高レベルの要点は次のとおりです。スレッドセーフでは、マルチスレッドの実行中に仕様が true を保持する必要があります。これを実際にコーディングするには、変更可能な共有状態へのアクセスを規制するという 1 つのことだけを行う必要があります3。そして、それを行うには3つの方法があります:

  • アクセスを防ぎます。
  • 状態を不変にします。
  • アクセスを同期します。

最初の 2 つは単純です。3 つ目は、次のスレッド セーフの問題を防ぐ必要があります。

  • 活力
    • デッドロック: 2 つのスレッドが、互いに必要なリソースを解放するのを永久にブロックします。
    • livelock : スレッドはビジー状態ですが、処理を進めることができません。
    • 飢餓: スレッドは、進行するために必要なリソースへのアクセスを永続的に拒否されます。
  • 安全なパブリケーション: パブリッシュされたオブジェクトの参照と状態の両方が、同時に他のスレッドから見えるようにする必要があります。
  • 競合状態競合状態は、出力が制御不能なイベントのタイミングに依存する欠陥です。つまり、正しい答えを得ることが幸運なタイミングに依存している場合、競合状態が発生します。どの複合操作も競合状態に陥る可能性があります。たとえば、「check-then-act」、「put-if-absent」などです。問題の例は であり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 不変の共有状態、または可変の非共有オブジェクトは、常にスレッドセーフです。

于 2013-03-13T18:32:45.373 に答える
4

@synchronized だけではコードをスレッド セーフにすることはできませんが、スレッド セーフ コードを記述する際に使用されるツールの 1 つです。

マルチスレッド プログラムでは、複雑な構造を一貫した状態に維持し、一度に 1 つのスレッドだけにアクセスさせたい場合がよくあります。一般的なパターンは、ミューテックスを使用して、構造体がアクセスまたは変更されるコードのクリティカル セクションを保護することです。

于 2013-03-13T18:10:43.110 に答える
3

@synchronizedthread safeメカニズムです。この関数内に記述されたコードはcritical section、一度に 1 つのスレッドしか実行できない の一部になります。

@synchronizeロックを暗黙的に適用するのに対しNSLock、明示的に適用します。

スレッドの安全性を保証するだけで、それを保証するものではありません。私が言いたいのは、あなたの車のために専門のドライバーを雇うということですが、それでも車が事故に遭わないという保証はありません。ただし、確率はごくわずかです。

同行者GCD(グランドセントラル発送)は着払いですdispatch_once。dispatch_once は to と同じ働きをし@synchronizedます。

于 2015-01-08T09:08:30.757 に答える
1

この@synchronizedディレクティブは、Objective-C コードでオンザフライでミューテックス ロックを作成する便利な方法です。

ミューテックス ロックの副作用:

  1. デッドロック
  2. 飢餓

@synchronizedスレッドの安全性は、ブロックの使用法に依存します。

于 2013-03-14T07:06:28.807 に答える