多线程模型下,因为共享内存带来的冲突风险,锁是个避不开的话题。java
首先从平台无关的角度看,从能力上区分,主要有如下几种锁:ios
全部的锁,语义上基本就这几种,git
以API提供者的维度梳理一下iOS的锁github
内核安全
pthread:POSIX标准真的是大而全...啥都有多线程
GCD并发
Cocoa Foundationasync
objc runtime函数
以上,相对全面地列举了iOS中的锁,它们是不一样层级的库提供的,但因为iOS中全部的线程本质上都是内核级线程,所以这些锁是可以公用的。post
环境:iPhone 7 plus + iOS 11
基于YY老师 再也不安全的 OSSpinLock 中的性能对比代码,加入了os_unfair_lock
,从新跑的一个性能对比。测试代码在这里
上面测试的是纯粹的加锁解锁性能,中间没有任何逻辑也不存在多线程抢占,为了更贴合咱们的实际环境,我构造了一个简单的多线程环境:NSOperationQueue
最大并发数为10,建立10个NSOperation
,每一个NSOperation
作10w次i++操做,每次操做加锁,代码在这里,结果以下:
能够看到多线程抢占的情形下结果跟前面略有不一样,在真实业务场景下这个数据应该更有参考意义。
因为OSSpinLock存在的优先级反转问题,已经废弃再也不使用。(参考:再也不安全的 OSSpinLock )
@synchronized
。使用最方便。通常业务开发场景,锁的性能影响不大,能力上也只须要简单的互斥锁,所以怎么方便怎么来。并且@synchronized
性能也没有差太多。os_unfair_lock
,自旋锁废弃后官方推荐的替代品,性能优异。dispatch_semaphore
NSCondition
pthread_rwlock
NSRecursiveLock
自旋锁是这些锁中惟一一个依靠忙等待实现的锁,也就是说能够理解成一个暴力的while循环,所以会浪费较多的CPU,但它是全部锁中性能最高的。适用于对时延要求比较苛刻、临界区计算量比较小、自己CPU不存在瓶颈的场景。
可是如今不能用了。YY老师在再也不安全的 OSSpinLock 中讲得很清楚了,当低优先级的线程已进入临界区,高优先级的线程想要获取资源就须要忙等待,占用大量CPU,致使低优先级线程迟迟不能执行完临界区代码,致使类死锁的问题。
OSSpinLock lock = OS_SPINLOCK_INIT; OSSpinLockLock(&lock); // do something OSSpinLockUnlock(&lock);
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; os_unfair_lock_lock(&lock); // do something os_unfair_lock_unlock(&lock);
pthread_mutex_t lock; pthread_mutex_init(&lock, NULL); pthread_mutex_lock(&lock); // do something pthread_mutex_unlock(&lock);
dispatch_semaphore_t lock = dispatch_semaphore_create(1); dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); // do something dispatch_semaphore_signal(lock);
dispatch_semaphore_create
传入参数是信号量的值,在这里就是可以同时进入临界区的线程数。dispatch_semaphore_wait
,当信号量大于0时减一并进入临界区,若是信号量等于0则等待直到信号量不为0或到达设定时间。dispatch_semaphore_signal
使信号量加1。
NSLock *lock = [NSLock new]; [lock lock]; // do something [lock unlock];
条件锁,以生产者消费者模型为例
NSCondition *condition = [NSCondition new]; // Thread 1: 消费者 - (void)consumer { [condition lock]; while(conditionNotSatisfied){ [condition wait] } // 消费逻辑 consume(); [condition unlock]; } // Thread 2: 生产者 - (void)producer { [condition lock]; // 生产逻辑 produce(); [condition signal]; [condition unlock]; }
条件锁,跟NSCondition差很少,对条件作了封装,简化了使用但也没NSCondition那么灵活了。
NSConditionLock *condition = [[NSConditionLock alloc] initWithCondition:1]; // Thread 1: 消费者 - (void)consumer { [condition lockWhenCondition:1]; while(conditionNotSatisfied){ [condition wait] } // 消费逻辑 consume(); [condition unlockWithCondition:0]; } // Thread 2: 生产者 - (void)producer { [condition lock]; // 生产逻辑 produce(); [condition unlockWithCondition:1]; }
能够递归调用的互斥锁。
int i = 0; NSRecursiveLock *lock = [NSRecursiveLock new]; - (void)testLock { if(i > 0){ [lock lock]; [self testLock]; i --; [lock lock]; } }
普通的锁,用着方便。
@synchronized(self) { // do something }
读写锁,通常也不怎么用得上,这里给了个字典set/get的例子,可是实际业务场景,一般普通的互斥锁就能够了。
在读操做比写操做多不少的状况下,读写锁的收益比较可观。
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; NSMutableDictionary *dic = [NSMutableDictionary new]; - (void)set { // 写模式加锁 pthread_rwlock_wrlock(&lock); dic[@"key"] = @"value"; // 解锁 pthread_rwlock_unlock(&lock); } - (NSString *)get { NSString *value; // 写模式加锁 pthread_rwlock_rdlock(&lock); value = dic[@"key"]; // 解锁 pthread_rwlock_unlock(&lock); return value; }
推荐阅读