Почему происходит сбой при изменении NSMutableSet в нескольких потоках, однако изменение пользовательского объекта Person в тех же потоках не приводит к сбою? - PullRequest
1 голос
/ 10 марта 2019

Ниже приведен код.Внутри блока я перепробовал 3 случая.Только один случай за один раз комментируется.

set = [[NSMutableSet alloc] init];
Person* p = [[Person alloc] init];

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
for (int index = 0; index < 100; index++) {
    dispatch_async(queue, ^{

            //Case 1: adding constant value each time
            //[self->set addObject:@"0"];

            //Case 2: adding new value on each call.
            [self->set addObject:[NSNumber numberWithInt:index]];

            //Case 3: Modifying custom object
             // p.firstname = [NSString stringWithFormat:@"%d", index];
    });

}

Я знаю, если я поставлю блокировку / @ syncronize внутри блока, она будет работать нормально.

Вопрос 1 : почему первый вариант запускается без проблем, даже когда я пытался с диапазоном индекса от 0 до 100000. Изменяемый набор не является потокобезопасным, поэтому он должен где-то аварийно завершать работу.Так почему же он работает без синхронизации?

Вопрос 2 : так как случай 1 работает нормально каждый раз, поэтому случай 2 также должен работать нормально, так как изменяется тот же набор.Но он вылетает при каждом запуске.

Вопрос 3 : Случай 3 не падает, даже если я добавляю новое значение каждый раз, как во втором случае.

Примечание: все свойстванеатомные.

Журналы сбоев в случае 2: ​​

**malloc: *** **error for object 0x6000028e9260: pointer being freed was not allocated****

1 Ответ

4 голосов
/ 10 марта 2019

Во-первых, давайте проясним, что ничего из этого не является поточно-ориентированным.Синхронизация необходима, если вы действительно собираетесь делать это из нескольких потоков.

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

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

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

Кроме того, синхронизация пользовательского объекта, такого как «человек», обычно должна происходить на более высоком уровне абстракции (например, при изменении полного имени человека при одновременном чтении действительно следует ожидать всех трех свойств, во-первых, среднего).и фамилию, чтобы закончить, или иначе вы можете поймать это в каком-то неопределенном состоянии).

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


Я бы посоветовал вам исследовать средство очистки потока (TSAN, его друзьям).Итак, в Xcode, перейдите в «Редактор», «Схема», «Редактировать схему ...» и включите средство очистки потока:

enter image description here

Thisпоможет вам определить небезопасный доступ из нескольких потоков.Для получения дополнительной информации см. Видео Thread Sanitizer и Static Analyzer .

Но, как Apple советует в приведенном выше видео, «нет такой вещи, как« мягкая »гонка».

...