1

GCD を介して にアクセスする 2 つの異なるスレッドがNSMutableArrayあり、一方のスレッドが変更可能な配列に基づいて新しい配列を作成し、もう一方のスレッドが配列からレコードを削除している場合、これは問題になると予想できますか? つまり、単に配列を「読み取っている」と思われるコピーは、その時点で配列にあるものを取得するべきではありませんか? どちらのスレッドでも配列を列挙していませんが、まだクラッシュしています。読み取りルーチンを削除するとすぐに、正常に動作します。

これが「読み取り」です:

  dispatch_async(saveQueue, ^{

    NSDictionary*tempstocks=[NSDictionary dictionaryWithDictionary:self.data];

このスレッドで次のようにクラッシュします。*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[9]'

別のスレッドで何が起こっているかを次に示します。

[self.data removeObjectForKey:item];

列挙中に変更できないことはわかっていますが、変更中に読んでも大丈夫だと思います。取得した変更されたオブジェクトのバージョンがわからない場合がありますが、これは問題ではないと思いますが、明らかにそうです. おそらく、dictionaryWithDictionaryメソッドは最初に X オブジェクトを参照する操作を実行していますが、ルーチンが完了するまでに XY オブジェクトが含まれているため、self.data実行時に 1 回のスナップでディクショナリ全体を「キャプチャ」するのではなく、本質的にどれが列挙されるかdictionaryWithDictionaryを列挙しています。self.data列挙中の突然変異と同じ問題?

4

3 に答える 3

5

GCD を使用して 3 つの異なるキューを作成する可能性があると思いますNSMutableArray

dispatch_async(saveQueue, ^{
    dispatch_barrier_async(_queue, ^{
            NSDictionary*tempstocks=[NSDictionary dictionaryWithDictionary:self.data];
        });
});

dispatch_async(anotherQueue, ^{
    dispatch_barrier_async(_queue, ^{
            [self.data removeObjectForKey:item];
        });
});

それは似@synchronizeていますが、GCDを使用しています。

詳細: GCD Reference/dispatch_barrier_asyncおよびhttp://www.mikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html

編集

どちらの方法が速いかを理解するために、いくつかのパフォーマンステストを行いました。

- (void)usingSynchronized
{
    dispatch_queue_t writeQyeue = dispatch_queue_create("com.tikhop.writeQyeue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(writeQyeue, ^{
        for(size_t i=0; i<10000; i++)
            @synchronized (arr) {
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:1]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:2]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:3]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:4]];
            }
    });
}

- (void)usingGCD
{
    dispatch_queue_t writeQyeue = dispatch_queue_create("com.tikhop.writeQyeue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(writeQyeue, ^{
        for(size_t i=0; i<10000; i++)
            dispatch_barrier_async(_queue, ^{
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:5]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:6]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:7]];
                [arr replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:8]];
            });
    });
}

arr = [NSMutableArray arrayWithCapacity:1];
[arr addObject:@(0)];

[self usingSynchronized];
[self usingGCD];

次の結果が得られました。 ここに画像の説明を入力

于 2012-08-12T09:20:51.147 に答える
1

NSDictionary での操作がスレッドセーフであると想定することはできません。そして、それらのほとんどすべてがそうではありません。ミューテックスを設定する@synchronizeか、アレイにアクセスするか、アクセスに gcd シリアル キューを使用する必要があります。

于 2012-08-12T09:13:27.393 に答える
0

dictionaryWithDictionary:は引数を内部的に列挙しているため、基本的に列挙中に変更します。

また、一般に、ある種の同期プリミティブを使用しない限り、別のスレッドが何らかの方法でオブジェクトにアクセスする場合は、オブジェクトに書き込みを行わないでください。

現時点で存在するものが何であれ、それが「読み取る」というあなたの推論は、一般的には有効ではありません。マルチスレッドに固有の問題に関するもう少し情報がありますマルチスレッドプログラムでのコンパイラによるレジスタの使用

于 2012-08-12T09:28:58.653 に答える