Коллекция NSMutableArray и @Синхронизированные блоки
В Objective C я использую экземпляр NSMutableArray из разных потоков, и я использую @synchronized, чтобы сделать его потокобезопасным. в настоящее время все мои обращения к этому массиву защищены блоком @synchronized, даже методом objectAtIndex:. Тем не менее, мне интересно, какие методы действительно требуют защиты с помощью @synchronized. мне нужно защитить доступ для чтения?
Что произойдет, если "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
Ответы
Ответ 1
Да, вам нужно синхронизировать обращения чтения, чтобы предотвратить их одновременное с мутациями. Однако доступ к чтению может безопасно запускаться одновременно с другими доступом для чтения.
Если у вас несколько читателей, то стоит изучить схему блокировки чтения-записи. Вы можете использовать блокировки чтения и записи pthread (т.е. pthread_rwlock_...()
).
В качестве альтернативы вы можете использовать GCD на OS X 10.7+ и iOS 5+ с помощью "барьерных" подпрограмм. Создайте приватную параллельную очередь. Отправьте все операции чтения на него обычным способом (например, dispatch_sync()
). Отправьте ему мутационные операции, используя барьерную процедуру, такую как dispatch_barrier_async()
. (Это может быть асинхронно, потому что вам обычно не нужно знать, что мутация завершилась до продолжения. Вам нужно только знать, что все прочитанные, после подачи мутации, будут видеть результаты мутации, и этот барьер гарантирует это. )
Вы можете узнать больше об этом, если у вас есть доступ к видеозаписям WWDC 2011 для сеанса 210 - Освоение Grand Central Dispatch.
Ответ 2
Вы должны знать, что то, что вы делаете, на самом деле не очень помогает. Например, если ваш массив имеет десять элементов, и вы вызываете [myObject objectAtIndex: 9], а другой поток вызывает [myObject removeObject: someObject], то, скорее всего, первый вызов обращается к элементу массива, который больше не существует и выдает исключение,
objectAtIndex не очень полезен, если другие потоки могут удалять или вставлять объекты.