Как @synchronized блокировать / разблокировать в Objective-C? - PullRequest
194 голосов
/ 01 августа 2009

Разве @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];
}

Ответы [ 5 ]

311 голосов
/ 01 августа 2009

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

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

40 голосов
/ 01 августа 2009

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

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

31 голосов
/ 18 мая 2011

На самом деле

{
  @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>
3 голосов
/ 09 августа 2017

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

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

0 голосов
/ 01 августа 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...