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

Использует ли @синхронизировать не использование "блокировки" и "разблокировки" для достижения взаимного исключения? Как он блокируется/разблокируется?

Вывод следующей программы - это только "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];
}

Ответ 1

Синхронизация языкового уровня 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;
}

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

Ответ 2

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

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

Ответ 3

На самом деле

{
  @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>

Ответ 4

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

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

Ответ 5

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