52

私はこのクラッシュをほぼ1週間修正しようとしています。アプリケーションは、例外やスタックトレースなしでクラッシュします。ゾンビモードで機器を実行している間、アプリケーションがクラッシュすることはありません。

別のスレッドで呼び出されるメソッドがあります。クラッシュを修正した解決策は、

[self.mutableArray removeAllObjects];

dispatch_async(dispatch_get_main_queue(), ^{
    [self.searchResult removeAllObjects];
});

タイミングの問題かもしれないと思ったので、同期しようとしましたが、それでもクラッシュしました:

@synchronized(self)
{
    [self.searchResult removeAllObjects];
}

これがコードです

- (void)populateItems
{
   // Cancel if already exists  
   [self.searchThread cancel];

   self.searchThread = [[NSThread alloc] initWithTarget:self
                                               selector:@selector(populateItemsinBackground)
                                                 object:nil];

    [self.searchThread start];
}


- (void)populateItemsinBackground
{
    @autoreleasepool
    {
        if ([[NSThread currentThread] isCancelled])
            [NSThread exit];

        [self.mutableArray removeAllObjects];

        // Populate data here into mutable array

        for (loop here)
        {
            if ([[NSThread currentThread] isCancelled])
                [NSThread exit];

            // Add items to mutableArray
        }
    }
}

NSMutableArrayがスレッドセーフではないというこの問題はありますか?

4

7 に答える 7

90

いいえ。

スレッドセーフではありません。別のスレッドから可変配列を変更する必要がある場合は、NSLockすべてが計画どおりに行われるようにするために使用する必要があります。

NSLock *arrayLock = [[NSLock alloc] init];

[...] 

[arrayLock lock]; // NSMutableArray isn't thread-safe
[myMutableArray addObject:@"something"];
[myMutableArray removeObjectAtIndex:5];
[arrayLock unlock];
于 2012-08-23T18:43:51.183 に答える
39

他の人がすでに言ったように、NSMutableArrayはスレッドセーフではありません。スレッドセーフな環境でremoveAllObject以上のことを達成したい場合は、ロックを使用するソリューション以外に、GCDを使用する別のソリューションを提供します。あなたがしなければならないことは、読み取り/更新(置換/削除)アクションを同期させることです。

まず、グローバル並行キューを取得します。

dispatch_queue_t concurrent_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

読むために:

- (id)objectAtIndex:(NSUInteger)index {
    __block id obj;
    dispatch_sync(self.concurrent_queue, ^{
        obj = [self.searchResult objectAtIndex:index];
    });
    return obj;
}

挿入の場合:

- (void)insertObject:(id)obj atIndex:(NSUInteger)index {
    dispatch_barrier_async(self.concurrent_queue, ^{
        [self.searchResult insertObject:obj atIndex:index];
    });
}

dispatch_barrier_asyncについてのAppleDocから:

バリアブロックがプライベート並行キューの先頭に到達しても、すぐには実行されません。代わりに、キューは現在実行中のブロックの実行が終了するまで待機します。その時点で、バリアブロックはそれ自体で実行されます。バリアブロックの後に送信されたブロックは、バリアブロックが完了するまで実行されません。

削除についても同様です。

- (void)removeObjectAtIndex:(NSUInteger)index {
    dispatch_barrier_async(self.concurrent_queue, ^{
        [self.searchResult removeObjectAtIndex:index];
    });
}

編集:実際、私は今日、GCDによって提供されるシリアルキューを使用してリソースへのアクセスを同期する別のより簡単な方法を見つけました。

Apple Doc同時実行プログラミングガイド>ディスパッチキューから:

シリアルキューは、タスクを特定の順序で実行する場合に役立ちます。シリアルキューは一度に1つのタスクのみを実行し、常にキューの先頭からタスクをプルします。共有リソースまたは可変データ構造を保護するために、ロックの代わりにシリアルキューを使用する場合があります。ロックとは異なり、シリアルキューは、タスクが予測可能な順序で実行されることを保証します。また、タスクを非同期でシリアルキューに送信する限り、キューがデッドロックすることはありません。

シリアルキューを作成します。

dispatch_queue_t myQueue = dispatch_queue_create("com.example.MyQueue", NULL);

ディスパッチタスクはシリアルキューに非同期です。

dispatch_async(myQueue, ^{
    obj = [self.searchResult objectAtIndex:index];
});

dispatch_async(myQueue, ^{
    [self.searchResult removeObjectAtIndex:index];
});

それが役に立てば幸い!

于 2013-07-31T22:16:30.340 に答える
21

condition-objectNSLock )を使用することもできますが、同じ配列インスタンスのコンテンツのみを変更する場合は、配列のすべてのアクセスが、 condition-objectとして機能する同じオブジェクトでラップされていることを確認する必要があります。次に、配列自体を条件オブジェクトとして使用できます。そうでない場合は、消えないことがわかっている他のオブジェクトを使用する必要があります。親オブジェクト、つまりselfは、常に同じオブジェクトであるため、適切な選択です。同じ配列。@synchronized@synchronized

属性のアトミックは@property、内容を変更せずに配列スレッドを安全に設定するだけです。つまり、self.mutableArray= ...はスレッドセーフですが、[self.mutableArray removeObject:]そうではありません。

于 2012-09-07T12:05:10.350 に答える
14
__weak typeof(self)weakSelf = self;

 @synchronized (weakSelf.mutableArray) {
     [weakSelf.mutableArray removeAllObjects];
 }
于 2014-05-02T19:38:44.113 に答える
5

シリアルキューが言及されたので:可変配列では、「スレッドセーフですか」と尋ねるだけでは十分ではありません。たとえば、removeAllObjectsがクラッシュしないことを確認することはすべて問題ありませんが、別のスレッドが同時に配列を処理しようとすると、すべての要素が削除されるまたはに配列が処理されるため、実際には振る舞いがどうあるべきかを考えてください。

この配列を担当する1つのクラス+オブジェクトを作成し、その配列のシリアルキューを作成し、そのシリアルキューでクラスを介してすべての操作を実行するのが、同期の問題で頭を悩ませることなく問題を解決する最も簡単な方法です。

于 2014-02-18T23:54:21.950 に答える
1

すべてのNSMutablexxxクラスはスレッドセーフではありません。get、insert、remove、add、replaceなどの操作は、NSLockで使用する必要があります。これは、アップルが提供するスレッドセーフクラスとスレッドセーフクラスのリストです。スレッドセーフの概要

于 2016-08-17T15:23:45.927 に答える
0

ほとんどのNSMutableクラスオブジェクトはスレッドセーフではありません。

于 2012-08-23T18:44:26.637 に答える