6

В Objective C Я использую экземпляр NSMutableArray из разных потоков, и я использую @synchronized, чтобы сделать его потокобезопасным. в настоящее время все мои обращения к этому массиву защищены блоком @synchronized, даже методом objectAtIndex :. Тем не менее, мне интересно, какие методы действительно требуют защиты с помощью @synchronized. мне нужно защитить доступ для чтения?Коллекция NSMutableArray и @ Синхронизированные блоки

Что произойдет, если «ObjectAtIndex» не будет защищен и вызван одновременно с «removeObject»?

Если все методы защищены с помощью @synchronized, что относительно производительности? (Я создаю игровой сервер tcp/udp и на самом деле не хочу перестраивать этот массив, если он уменьшит перфект или создаст блокировки).

Например, я полагаю, что метод 'containsObject:' будет перечислять для поиска объекта и что я должен избегать совпадающего вызова 'removeObject:' в другом потоке.

Возможно, хорошим решением будет иметь слишком разные замки (для чтения и записи) ...

Помощь и предложения приветствуются!

Большое спасибо.

Пожалуйста найти пример кода ниже для иллюстрации:

@interface TestClass : NSObject 
{ 
    NSMutableArray * array; 
} 

@end 

@implementation TestClass 

- (id)init 
{ 
    self = [super init]; 
    if (self) 
    { 
     array = [NSMutableArray array]; 
    } 
    return self; 
} 

-(id)objectAtIndex:(NSUInteger)index 
{ 
    @synchronized(array) **// IS IT USEFUL OR NOT ??** 
    { 
     return [array objectAtIndex:index]; 
    } 
} 

-(void)removeObject:(id)object 
{ 
    @synchronized(array) 
    { 
     [array removeObject:object]; 
    } 
} 

-(void)compute 
{ 
    @synchronized(array) 
    { 
     for (id object in array) 
     { 
      [object compute]; 
     } 
    } 
} 

@end 

ответ

5

Да, вы должны синхронизировать чтения доступ, чтобы предотвратить их одновременно происходит с мутациями. Однако доступ к чтению может безопасно работать одновременно с другими доступом для чтения.

Если у вас несколько читателей, то стоит изучить схему блокировки чтения-записи. Вы можете использовать блокировки чтения и записи pthread (т. Е. pthread_rwlock_...()).

В качестве альтернативы вы можете использовать GCD на OS X 10.7+ и iOS 5+ с помощью «барьерных» процедур. Создайте приватную параллельную очередь. Отправьте все операции чтения на него обычным способом (например, dispatch_sync()). Отправьте ему мутационные операции с использованием барьерной процедуры, такой как dispatch_barrier_async(). (Это может быть асинхронно, потому что вам обычно не нужно знать, что мутация завершилась, прежде чем продолжить. Вам нужно только знать, что все прочитанные после отправки мутации будут видеть результаты мутации, и барьер гарантирует это.)

Вы можете узнать больше об этом, если у вас есть доступ к WWDC 2011 session video for Session 210 - Mastering Grand Central Dispatch.

+0

Мне очень нравится решение GCD. Поскольку я использую многие изменчивые массивы (и словарь тоже), которые я бы хотел защитить, я задаюсь вопросом, могу ли я создать свой собственный GCDMutableArray и GCDMutableDictionary, которые переопределили бы все методы и добавили dispatch_queue_t для каждого экземпляра массива или словаря. Но является ли это хорошим решением, чтобы иметь 10 изменяемых словарей со своим собственным dispatch_queue? А для создания отдельной очереди в каждом экземпляре? dispatch_queue_create принимает имя как arg, как сгенерировать это имя? Возможно, этот вид защищенного массива и dict уже был написан ... но ничего не может найти. –

+0

Я не думаю, что такие классы, как правило, полезны. Практически никогда не бывает проблем с безопасностью потоков, концентрируясь на низкоуровневых классах, таких как контейнеры. Вы почти наверняка должны анализировать свои классы на уровне своей абстракции, чтобы * конструировать * свои интерфейсы, а не только их реализации, для обеспечения безопасности потоков. Тем не менее, очереди отправки дешевы, и Apple поощряет вас создавать столько, сколько требует разумный дизайн. –

0

Вы должны знать, что то, что вы делаете, на самом деле не очень помогает. Например, если ваш массив имеет десять элементов, и вы вызываете [myObject objectAtIndex: 9], а другой поток вызывает [myObject removeObject: someObject], то, скорее всего, первый вызов обращается к элементу массива, который больше не существует и выдает исключение ,

objectAtIndex не очень полезен, если другие потоки могут удалять или вставлять объекты.

Смежные вопросы