Как @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блокировок, которые блокируются и разблокируются по мере необходимости.


-4

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


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

3
Не просто мьютекс, а рекурсивная блокировка.
kperryua
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.