Коллекция 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 не очень полезен, если другие потоки могут удалять или вставлять объекты.