Objective-C中的同步locking/解锁是如何实现的?
@synchronized不使用“locking”和“解锁”实现互斥? 那么它是如何locking/解锁的呢?
以下程序的输出只是“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
你有一个隐含的锁与你用来同步的对象相关联。 语言级别locking的好处是编译器理解它,因此它可以处理范围问题,但机械上他们的行为基本相同。
你可以把@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; }
这是不完全正确的,因为实际的转换更复杂,并使用recursionlocking,但它应该得到的重点。
在Objective-C中,一个@synchronized
块为你自动处理locking和解锁(以及可能的例外)。 运行时dynamic地生成一个与正在同步的对象相关联的NSRecursiveLock。 这个Apple文档更详细地解释了它。 这就是为什么你没有看到你的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实现是开源的,可以在这里find。 Mike Ash写了两篇关于这个主题的非常有趣的文章:
- 锁,线程安全和Swift
- 让我们build立@synchronized
简而言之,它有一个表,它将对象指针(使用它们的内存地址作为关键字)映射到pthread_mutex_t
锁,这些锁根据需要被locking和解锁。
它只是将一个信号与每个对象关联起来,并使用它。