Вопрос по cocoa, grand-central-dispatch, concurrency – Мутирующий массив при чтении, не перечисляя

1

Если у меня есть два разных потока через GCD доступ к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, но к тому времени, когда процедура выполнена, она содержит объекты X-Y, таким образом, она не "захватывает" целикомself.data словарь в одно мгновение при запускеdictionaryWithDictionary и вместо этого перечисляетself.data что по существу будет той же проблемой, что и мутация при перечислении?

У вас есть код? Я хотел бы увидеть код. bitmapdata.com
Попробуй этоmikeash.com/pyblog/friday-qa-2011-10-14-whats-new-in-gcd.html tikhop
Какое сообщение об ошибке вы получаете при сбое? Josh Caswell
обновленный вопрос с кодом и деталями ошибки johnbakers

Ваш Ответ

3   ответа
1

что любая операция на NSDictionary является поточно-ориентированной. И почти все они не такие. Вам действительно нужно настроить мьютекс,@synchronize доступ к вашему массиву или использовать последовательную очередь gcd для доступа.

Может ли это быть так просто, как этот ответ:stackoverflow.com/a/4676307/768472 johnbakers
5

что вы можете создать три разные очереди, используя GCD: одну для сохранения, вторую для чего-то другого и последнюю для работы с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

EDIT

Я сделал пару тестов производительности, чтобы понять, какой из способов быстрее:

- (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];

Я получил следующий результат: enter image description here

Я предполагаю, что оба подхода делают то же самое, но GCD быстрее.
Хороший тест! что@(0) в вашемaddObject метод? Также является_queue определяется как параллельная очередь? johnbakers
Мне это нравится. В большинстве документов и советов, которые я читаю, предлагается использовать ГКД в максимально возможной степени, исключая@synchronize так что это хорошее предложение johnbakers
также мы можем просто использовать dispatch_sync для чтения self.data
Ваш ответ имеет смысл для меня, но я признаю, что также очень легко сделать то, что предлагается здесь:stackoverflow.com/a/4676307/768472 Считаете ли вы, что ответ дает достаточно решения для использования@synchronize метод? johnbakers
0

внутренне перечисляет аргумент, поэтому при перечислении вы в основном мутируете.

Кроме того, в общем, вы никогда не должны писать в объект, если другой поток собирается получить к нему какой-либо доступ, если вы не используете какой-то примитив синхронизации.

Ваши рассуждения о том, что он "читает" что бы там ни было в данный момент, вообще не действует. Вот еще немного информации о проблемах, присущих многопоточностиИспользование регистров компилятором в многопоточной программе

Похожие вопросы