@synchronized不使用“lock”和“unlock”实现互斥吗?那么它是如何锁定/解锁的呢?

下面程序的输出仅仅是“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];
}

它只是将一个信号量与每个对象关联起来,并使用它。


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;
}

这不是完全正确的,因为实际的转换更复杂,并且使用递归锁,但它应该能够理解这一点。


在Objective-C中,@synchronized块自动为你处理锁定和解锁(以及可能的异常)。运行时动态地生成一个NSRecursiveLock,它与你正在同步的对象相关联。这个苹果文档更详细地解释了它。这就是为什么你看不到来自NSLock子类的日志消息——你同步的对象可以是任何东西,而不仅仅是一个NSLock。

基本上,@synchronized(…)是一个简化代码的方便构造。像大多数简化抽象一样,它也有相关的开销(可以将其视为隐藏成本),了解这一点是很好的,但是在使用这种结构时,原始性能可能不是最高目标。


实际上

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

苹果的@synchronized实现是开源的,可以在这里找到。Mike ash写了两篇关于这个主题的有趣文章:

锁,线程安全,和Swift 让我们构建@synchronized

简而言之,它有一个将对象指针(使用它们的内存地址作为键)映射到pthread_mutex_t锁的表,pthread_mutex_t锁根据需要锁定和解锁。