3

スレッドセーフに問題がありました。コンテンツを変更するとスレッド間でエラーが発生するキューがあります。以前はロックを使用したことがありませんでしたが、試してみました。キューのバッキング NSMutableArray を操作するすべてのコードにロックを追加しました。問題は、私がそれらすべてに同じロックを使用しなかったことだと思います。配列を変更した各メソッドで NSLock の新しいインスタンスを作成しました。アレイを保護するには、1 つの NSLock ivar を使用する必要があると想定しています。しかし、私の混乱は、一度追加すると機能したという事実から来ています。以下はサンプルです。新しい NSLock を作成したすべての場所で、ivar NSLock を 1 つだけ使用する必要があったと思います。このコードは、デキューに対するエンキューではなく、他のエンキューに対するエンキューと他のデキューに対するデキューをロックしただけだと思います。明確化は素晴らしいでしょう。

@implmentation

...    

- (void)enqueue:(id)obj
{
  NSLock *arrayLock = [[NSLock alloc] init];
  [arrayLock lock];
   [_backingStore addObject:obj];
  [arrayLock unlock];
}

- (id)dequeue
{
  NSLock *arrayLock = [[NSLock alloc] init];
  [arrayLock lock];
  id result = [_backingStore firstObject];

  if( result )
  {
    [_backingStore removeObjectAtIndex:0];
  }

  [arrayLock unlock];
  return result;
}

...

@end
4

1 に答える 1

6

はい、アレイへの両方のアクセスをロックするには、同じ NSLock インスタンスを使用する必要があります。非常に多くのマルチスレッド エラーと同様に、コードを追加したことによるタイミングの違いにより、問題が解消されたように見えた可能性があります。または、運が (不運) で、2 回目のテストで問題が発生しなかった可能性もあります。

なんにせよ、NSLock は Objective-C のクリティカル セクションへのアクセスをロックする 1 つの方法にすぎません。@synchronized()コードの複雑さの観点から、どちらが簡単かを使用することもできます。

@synchronized(someSharedToken) {
    // Do your array access here
}

シリアル ディスパッチ キューを使用して、リソースへのアクセスをシリアル化することもできます。これにはいくつかの利点がありますが、その中でも特に、現在のスレッドで作業が完了するのを待たずに作業をディスパッチできることが挙げられます。また、ロックを取得するよりも、作業をキューにディスパッチする方がコストがかかりません。Apple の同時実行プログラミング ガイドの「シリアル ディスパッチ キューの作成」セクションを参照してください。

于 2013-10-18T16:12:22.670 に答える