Неправильное использование NSLock, но все еще работает

У меня были проблемы с безопасностью потоков. У меня есть очередь, которая, когда я изменил содержимое, вызвала ошибки в потоках. Раньше я не использовал замки, но решил попробовать. Я добавил блокировку всего кода, который манипулировал резервным массивом NSMutableArray для моей очереди. Проблема, я думаю, в том, что я не использовал один и тот же замок для всех из них. Я создал новый экземпляр NSLock в каждом методе, который модифицировал массив. Я предполагаю, что мне следует использовать один NSLock ivar для защиты массива. Но мое замешательство связано с тем, что это сработало, как только я его добавил. Ниже приведен образец. Я предполагаю, что везде, где я создавал новый NSLock, я должен был просто использовать один ivar NSLock. Я бы подумал, что этот код просто заблокировал очереди против других очередей и очереди против других очередей, а не очереди против очередей. Уточнение было бы здорово.

@implmentation

...    

- (void)enqueue:(id)obj
{
  NSLock *arrayLock = [[NSLock alloc] init];
  [arrayLock lock];
   [_backingStore addObject:obj];
  [arrayLock unlock];
}

- (id)dequeue
{
  NSLock *arrayLock = [[NSLock alloc] init];
  [arrayLock lock];
  id result = [_backingStore firstObject];

  if( result )
  {
    [_backingStore removeObjectAtIndex:0];
  }

  [arrayLock unlock];
  return result;
}

...

@end

person Brian    schedule 18.10.2013    source источник


Ответы (1)


Да, вам нужно использовать один и тот же экземпляр NSLock, чтобы заблокировать оба доступа к массиву. Как и в случае со многими ошибками многопоточности, проблема могла исчезнуть из-за разницы во времени, вызванной добавлением дополнительного кода. Или, может быть, вам просто (не)повезло, и проблема не появилась при тестировании во второй раз.

Как бы то ни было, NSLock — это всего лишь один из способов блокировки доступа к критическим разделам в Objective-C. Вы также можете использовать @synchronized(), что может быть проще с точки зрения сложности кода:

@synchronized(someSharedToken) {
    // Do your array access here
}

Вы также можете использовать последовательную очередь отправки для сериализации доступа к ресурсу. У этого есть несколько преимуществ, не последним из которых является возможность отправлять ему работу, не дожидаясь завершения этой работы в текущем потоке. Кроме того, дешевле отправить работу в очередь, чем снять блокировку. См. Создание очередей последовательной отправки в Руководстве по программированию параллелизма от Apple.

person Andrew Madsen    schedule 18.10.2013
comment
Спасибо за ответ. Планирую перейти на очередь отправки. Я думал об использовании @synchronized, но думал, что это связано с дополнительными накладными расходами на обработку исключений. Поскольку должен использоваться один и тот же экземпляр, означает ли это в моем примере, что если два потока вызывают метод постановки в очередь, то ни один из них не будет фактически блокировать другой, поскольку каждый вызов метода создает свой собственный экземпляр? - person Brian; 18.10.2013
comment
да. Вы можете видеть это на быстром и грязном примере, который я привел здесь: . - person Andrew Madsen; 18.10.2013
comment
Спасибо за пример. - person Brian; 18.10.2013