Как @synchronized блокировать / разблокировать в Objective-C?

201

Разве @synchronized не использует «блокировку» и «разблокировку» для достижения взаимного исключения? Как это делает блокировку / разблокировку тогда?

Вывод следующей программы - только «Hello World».

@interface MyLock: NSLock<NSLocking>
@end

@implementation MyLock

- (id)init {
    return [super init];
}

- (void)lock {
    NSLog(@"before lock");
    [super lock];
    NSLog(@"after lock");
}

- (void)unlock {
    NSLog(@"before unlock");
    [super unlock];
    NSLog(@"after unlock");
}

@end


int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    MyLock *lock = [[MyLock new] autorelease];
    @synchronized(lock) {
        NSLog(@"Hello World");
    }

    [pool drain];
}
Дэвид Лин
источник
Примечание: относится к stackoverflow.com/questions/1215765
Куинн Тейлор
10
Вам не нужно переопределять init, если вам это не нужно. Среда выполнения автоматически вызывает реализацию суперкласса, если вы не переопределите метод.
Константино Царухас
3
Важно отметить, что приведенный выше код не синхронизирован. lockОбъект создается при каждом вызове, так что никогда не будет случай , когда один @synchronizedблок блокирует другой. И это означает, что взаимного исключения не существует.) Конечно, в приведенном выше примере выполняется операция main, поэтому в любом случае исключать нечего, но не следует слепо копировать этот код в другом месте.
Hot Licks
3
Прочитав эту SO страницу, я решил немного подробнее изучить @synchronized и написать в ней пост в блоге. Вы можете найти это полезным: rykap.com/objective-c/2015/05/09/synchronized
rjkaplan

Ответы:

323

Синхронизация на уровне языка Objective C использует мьютекс, как и NSLockделает. Семантически существуют некоторые небольшие технические различия, но в основном правильно думать о них как о двух отдельных интерфейсах, реализованных поверх общей (более примитивной) сущности.

В частности, у NSLockвас есть явная блокировка, тогда как у @synchronizedвас есть неявная блокировка, связанная с объектом, который вы используете для синхронизации. Преимущество блокировки на уровне языка заключается в том, что компилятор понимает ее, поэтому он может решать проблемы с областями видимости, но механически они ведут себя в основном одинаково.

Вы можете думать @synchronizedкак о переписывании компилятора:

- (NSString *)myString {
  @synchronized(self) {
    return [[myString retain] autorelease];
  }
}

превращается в:

- (NSString *)myString {
  NSString *retval = nil;
  pthread_mutex_t *self_mutex = LOOK_UP_MUTEX(self);
  pthread_mutex_lock(self_mutex);
  retval = [[myString retain] autorelease];
  pthread_mutex_unlock(self_mutex);
  return retval;
}

Это не совсем правильно, потому что фактическое преобразование является более сложным и использует рекурсивные блокировки, но оно должно понять смысл.

Луи Гербарг
источник
17
Вы также забываете об обработке исключений, которую делает для вас @synchronized. И, насколько я понимаю, большая часть этого обрабатывается во время выполнения. Это позволяет оптимизировать несанкционированные блокировки и т. Д.
Куинн Тейлор,
7
Как я уже сказал, фактический сгенерированный материал является более сложным, но мне не хотелось писать директивы секций для того, чтобы создавать таблицы раскрутки DWARF3 ;-)
Луи Гербарг,
И я не могу винить тебя. :-) Также обратите внимание, что OS X использует формат Mach-O вместо DWARF.
Куинн Тейлор
5
Никто не использует DWARF в качестве двоичного формата. OS X использует DWARF для символов отладки и использует таблицы разворачивания DWARF для исключений с нулевой стоимостью
Луи Гербарг,
7
Для справки я написал бэкенды компилятора для Mac OS X ;-)
Луи Гербарг
40

В Objective-C @synchronizedблок обрабатывает блокировку и разблокировку (а также возможные исключения) автоматически. Среда выполнения динамически по существу генерирует NSRecursiveLock, который связан с объектом, с которым вы синхронизируете. Эта документация Apple объясняет это более подробно. Вот почему вы не видите сообщений журнала от вашего подкласса NSLock - объект, с которым вы синхронизируете, может быть чем угодно, не только NSLock.

По сути, @synchronized (...)это удобная конструкция, которая упрощает ваш код. Подобно большинству упрощенных абстракций, он связан с накладными расходами (воспринимается как скрытая стоимость), и это хорошо осознавать, но в любом случае необработанная производительность, вероятно, не является высшей целью при использовании таких конструкций.

Куинн Тейлор
источник
1
Срок действия этой ссылки истек. Вот обновленная ссылка: developer.apple.com/library/archive/documentation/Cocoa/…
Ариэль Штайнер,
31

Фактически

{
  @synchronized(self) {
    return [[myString retain] autorelease];
  }
}

превращается непосредственно в:

// needs #import <objc/objc-sync.h>
{
  objc_sync_enter(self)
    id retVal = [[myString retain] autorelease];
  objc_sync_exit(self);
  return retVal;
}

Этот API доступен начиная с iOS 2.0 и импортирован с использованием ...

#import <objc/objc-sync.h>
Дирк Тайсен
источник
То есть он не поддерживает чистую обработку исключений?
Дастин
Это где-то задокументировано?
jbat100
6
Там есть несбалансированная скобка.
Potatoswatter
@Dustin на самом деле это делает из документов: «В качестве меры предосторожности @synchronizedблок неявно добавляет обработчик исключений в защищенный код. Этот обработчик автоматически освобождает мьютекс в случае возникновения исключения».
Питер
Вероятно, objc_sync_enter будет использовать мьютекс pthread, поэтому преобразование Луи более глубокое и правильное.
Джек,
3

Реализация @synchronized от Apple является открытым исходным кодом, и ее можно найти здесь . Майк Эш написал два действительно интересных поста на эту тему:

В двух словах, у него есть таблица, которая отображает указатели объектов (используя их адреса памяти в качестве ключей) для pthread_mutex_tблокировок, которые блокируются и разблокируются по мере необходимости.

JP Illanes
источник
-4

Он просто связывает семафор с каждым объектом и использует его.

Павел Минаев
источник
Технически это создает блокировку мьютекса, но основная идея верна. См. Apple Diva по адресу: developer.apple.com/documentation/Cocoa/Conceptual/…
Марк Бесси
3
Не просто мьютекс, а рекурсивная блокировка.
kperryua