Как сделать пользовательский класс потокобезопасным в Objective-C

#ios #objective-c #multithreading #thread-safety

#iOS #objective-c #многопоточность #потокобезопасность

Вопрос:

У меня есть a TableViewController , который использует элемент с индексом N для ячейки табличного представления в строке N. Поскольку к индексу массива N можно получить доступ из разных потоков, я создал ThreadSafeMutableArray класс, который выполняет чтение внутри a dispatch_sync и запись под a dispatch_barrier_async .

Предположим, я получаю объект с индексом N, скажем, используя Song *currSong = self.entries[N]; , а затем вношу изменения в свойства этого объекта. Правильно ли я понимаю, что мне нужно внести эти изменения потокобезопасным способом (потому что, например, tableview может запрашивать объект в ячейке N, и в то же время объект в ячейке N может быть обновлен, поскольку объект изображения, для которого он был получен из сети)? Если да, каков самый простой способ сделать мой пользовательский класс потокобезопасным?

Например: в этом ThreadSafeMutableArray случае я смог добиться этого, переопределив следующие методы и используя dispatch_sync и dispatch_barrier_async в рамках новой реализации методов.

 -(NSUInteger) count
-(id) objectAtIndex:(NSUInteger)index;
-(void) insertObject:(id)anObject atIndex:(NSUInteger)index;
-(void) removeObjectAtIndex:(NSUInteger)index;
-(void) addObject:(id)anObject;
-(void) removeLastObject;
-(void) replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
  

Комментарии:

1. Как вы думаете, почему ответ для вашего класса отличается от массива? Т.е. почему бы не использовать ту же модель синхронизации dispatch_(barrier_a)?

2. Спасибо @CRD. Единственная идея, которая приходит мне в голову, — создать свои собственные средства доступа для всех свойств и защитить их с помощью модели синхронизации dispatch_(barrier_a). Это лучший подход? Обычно я позволяю компилятору создавать мои средства доступа для моих свойств. Я слышал, что с аксессуарами могут возникнуть некоторые сложные проблемы. Есть ли какая-либо распространенная ошибка при создании средств доступа, о которой мне нужно беспокоиться?

Ответ №1:

Вам необходимо определить, что означает «потокобезопасный» в контексте вашего пользовательского класса / приложения. Возможно, вам просто нужна целостность данных, что означает, что ни один поток не видит недопустимое или частично сохраненное значение, например, подумайте об атомарных операциях чтения / записи; или вам может потребоваться целостность модели, например, где взаимосвязь нескольких элементов всегда правильная — как в вашем изменяемом массиве; или что-то между ними, например, подумайте обувеличение счетчика — это не так сложно, как поддерживать согласованность графика объектов, представляющих изменяемую структуру, но более сложно, чем простое атомарное чтение или запись. И т. Д. И т. Д. Безопасность потоков — большая тема!

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

Короче говоря, нет единого простого ответа. Изучите различные варианты, рассмотрите свои требования и выбирайте и выбирайте. Вы уже используете GCD для обеспечения потокобезопасности, это хорошо! Если вы придумали дизайн и у вас возникли проблемы с ним, вы всегда можете спросить ОБ ЭТОМ.

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

HTH

Ответ №2:

Самый простой способ добиться этого — создать единый метод доступа в вашем TableViewController и использовать @syncrhonized директиву для защиты доступа.

     - (void)updateObjectAt:(NSUInteger)index {
        @synchronized(itemArray) {
            // Everything between the braces is protected by the @synchronized directive.
            itemArray[index].update();
        }
    }
  

@synchornized Директива налагает блокировку на массив, все, что находится внутри блока кода, может безопасно получать доступ к элементам массива и изменять их. Если каким-либо другим методам необходимо получить доступ к массиву, просто оберните его в @syncrhonized блокировку массива.

Комментарии:

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

2. Если все эти операции обновления находятся внутри @syncrhonized блока с блокировкой itemArray, они безопасны. Для чтения вы можете сделать то же самое, или, если вам нужно, чтобы клиент прочитал и удерживал элемент, вы можете вернуть копию элемента (из @synchronized блока). Однако клиенту необходимо обновить себя.