我们平时开发中经常会用到多线程。多线程给我们带来了极大的便利,提高了程序的执行效率,但同时也带来了Datarace的定义,Datarace的定义很简单:Datarace发生在至少有两个线程同时访问同一个变量,并且在其中至少有一个是写操作。所以需要使用一些同步机制来保证数据的准确性,而锁就是同步机制之一。如何检测项目中的数据竞争?只需要在设置中勾选ThreadSanitizer,顺便勾选Pauseonissues,断点到对应的代码即可。下面进入正题,简单说一下iOS中的锁及相关内容(本人能力有限,文中难免有疏漏或错误之处,望指教!谢谢!)简单的性能测试图片下面是我自己测试的iOS中的锁。图中的数字代表每次添加和解锁所花费的时间,单位是ns。代码在这里,代码指的是YY大神不再安全的OSSpinLock,和YY大神的图基本类似??,YY大神的单位是μs,应该是1000次,还是写错了~LockPerformance.jpg注:运行手机:iphone6splus,系统版本:11.2.2,Xcode9.2;数字的单??位是ns(具体得到的值是多次运行后得到的平均值)。值得注意的是:1.这个数字只代表每次解锁的耗时,并不完全代表性能。2.机型和系统不同,周期时间不同,结果可能略有不同。但是仍然可以看出@synchronized:是表现最差的。在具体说这些锁之前,先说几个概念上的定义:(参考维基百科)临界区:指访问公共资源的一段代码,不是一种机制或算法。自旋锁:是用于多线程同步的锁。线程反复检查锁变量是否可用。由于这个过程中线程一直在执行,所以是一种忙等待。一旦获得自旋锁,线程就会持有该锁,直到它被显式释放。自旋锁避免了进程上下文的调度开销,因此它们对于线程只会阻塞很短时间的情况很有效。Mutex:是多线程编程中使用的一种机制,可以防止两个线程同时读写同一个公共资源(比如全局变量)。这个目标是通过将代码一个一个地分割成临界区来实现的。读写锁:是一种对计算机程序进行并发控制的同步机制,又称“共享-互斥锁”、多读-单-写锁),解决公网的多线程读写问题资源。读操作可以并发重入,写操作是互斥的。读写锁通常用互斥锁、条件变量和信号量来实现。信号量(semaphore):是一种比较高级的同步机制。互斥锁可以说是信号量的一个特例,它只取值0/1。信号量可以有更多的值空间来实现更复杂的同步,而不仅仅是线程之间的互斥。条件锁:它是一个条件变量。当进程的某些资源需求得不到满足时,它就会进入休眠状态,即被锁定。当资源分配完毕后,条件锁打开,进程继续运行。互斥锁1.NSLock:是Foundation框架中以对象的形式暴露给开发者的锁。(Foundation框架还提供了NSConditionLock、NSRecursiveLock、NSCondition)NSLock的定义如下:@protocolNSLocking-(void)lock;-(void)unlock;@end@interfaceNSLock:NSObject{@privatevoid*_priv;}-(BOOL)tryLock;-(BOOL)lockBeforeDate:(NSDate*)limit;@property(nullable,copy)NSString*nameAPI_AVAILABLE(macos(10.5),ios(2.0),watchos(2.0),tvos(9.0));@endtryLock和lock方法都会请求加锁,唯一的区别是trylock可以在没有获取到锁和处理的时候继续做一些任务。lockBeforeDate方法也比较简单,就是在limit时间点之前获取锁,没有获取到则返回NO。实际项目中:NSLock在AFNetworking的AFURLSessionManager.m中应用如下:-(instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration*)configuration{...self.lock=[[NSLockalloc]init];self.lock.name=AFURLSessionManagerLockName;.。[selfaddNotificationObserverForTask:task];[self.lockunlock];}2.pthread_mutex:现实项目中:在YYKit的YYMemoryCach中可以看到-(instancetype)init{...pthread_mutex_init(&_lock,NULL);...}-(void)_trimToCost:(NSUInteger)costLimit{BOOLfinish=NO;pthread_mutex_lock(&_lock);if(costLimit==0){[_lruremoveAll];finish=YES;}elseif(_lru->_totalCost<=costLimit){finish=YES;}pthread_mutex_unlock(&_lock);if(finish)return;NSMutableArray*holder=[NSMutableArraynew];while(!finish){if(pthread_mutex_trylock(&_lock)==0){if(_lru->_totalCost>costLimit){_YYLinkedMapNode*node=[_lruremoveTailNode];if(node)[holderaddObject:node];}else{finish=YES;}pthread_mutex_unlock(&_lock);}else{usleep(10*1000);//10毫秒}}...}3。@synchronized:实际项目中:AFNetworking中isNetworkActivityOccurring属性的getter方法-(BOOL)isNetworkActivityOccurring{@synchronized(self){returnsself.activityCount>0;}}自旋锁1.OSSpinLock:OSSpinLocklock=OS_SPINLOCK_INIT;OSSpinLockLock(&lock);...OSSpinLockUnlock(&lock);以上就是OSSpinLock的使用方法,编译会报warning,已经废弃了,OSSpinLock已经不再被大家使用了,因为已经在一些场景下使用了,已经不安全了,可以参考那个OSSpinLockYY神不再安全。在ProtocolBuffers项目中,可以看到这样的评论,大家已经换成了新的方案//NOTE:OSSpinLockmayseemlikeagoodfitherebutAppleengineershave//pointedoutthattheyarevulnerabletolivelockingoniOSincasesof//priorityinversion://http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe///https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372。html2.os_unfair_lock:os_unfair_lock是苹果官方推荐用来替代OSSpinLock的,但只能在iOS10.0以上系统上调用。os_unfair_lock_tunfairLock;unfairLock=&(OS_UNFAIR_LOCK_INIT);os_unfair_lock_lock(unfairLock);os_unfair_lock_unlock(unfairLock);读写锁上面说了读写锁也叫共享互斥锁,pthread_rwlock://加读锁pthread_rwlock_rdlock(&rwlock);//解锁pthread_rwlock_unlock(&rwlock);//加写锁pthread_rwlock_wrlock(&rwlock);//解锁pthread_rwlock_unlock(&rwlock);递归锁有一个特点,就是同一个线程可以被加锁N次而不会造成死锁。1.NSRecursiveLock:NSRecursiveLock在YYKit中的YYWebImageOperation.m中很有用:_lock=[NSRecursiveLocknew];-(void)dealloc{[_locklock];......[_lockunlock];}2.pthread_mutex(recursive):pthread_mutex锁也支持递归,只需要设置PTHREAD_MUTEX_RECURSIVE即可pthread_mutex_tlock;pthread_mutexattr_tattr;pthread_mutexattr_init(&attr);pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);pthread_mutex_init(&lock,&attr);pthread_mutexattr_destroy(&attr);pthread_mutex_lock(&lock);pthread_mutex_unlock(&lock);条件Lock1.NSCondition:定义:@interfaceNSCondition:NSObject{@privatevoid*_priv;}-(void)wait;-(BOOL)waitUntilDate:(NSDate*)limit;-(void)signal;-(void)broadcast;遵循NSLocking协议。使用的时候也是lock,unlock加unlock,wait傻了,waitUntilDate:方法就是等一会,线程会阻塞。signal是唤起一个等待线程,broadcast是广播所有被唤起。NSCondition*lock=[[NSConditionalloc]init];//线程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{[locklock];while(NoMoney){[lockwait];}NSLog(@"Themoneyhasbeenusedup.");[lockunlock];});//父线程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{[locklock];NSLog(@"Workhardtomakemoney.");[locksignal];[lockunlock];});2.NSConditionLock:定义:@interfaceNSConditionLock:NSObject{@privatevoid*_priv;}-(instancetype)initWithCondition:(NSInteger)conditionNS_DESIGNATED_INITIALIZER;@property(readonly)NSIntegercondition;-(void)lockWhenCondition:(NSInteger)condition;-(BOOL)tryLock;-(BOOL)tryLockWhenCondition:(NSInteger)条件;-(void)unlockWithCondition:(NSInteger)条件;-(BOOL)lockBeforeDate:(NSDate*)limit;-(BOOL)lockWhenCondition:(NSInteger)conditionbeforeDate:(NSDate*)limit;很简单,方法很清澈,基本相同。信号量dispatch_semaphore:dispatch_semaphore在YYKit中的YYThreadSafeArray.m中使用。YY大神有这样的评论:@discussionGenerally,accessperformanceislowerthanNSMutableArray,buthigherthanusing@synchronized,NSLock,orpthread_mutex_t.3#defineLOCK(...)dislockpatch_semaphore_TIME,DIT(;\__VA_ARGS__;\dispatch_semaphore_signal(_lock);总结:其实这篇文章写的一些基础的内容,在阅读一些开源项目的时候,经常会遇到一些保持线程同步的方法,因为不同的场景可能选择的模型不同,本文做一个简单的记录吧~相信看完本文后,你应该能够根据不同的场景选择合适的锁,能够分清自旋锁和互斥锁的区别最后:本人能力有限,文中难免有疏漏或错误之处,还请多多指教!谢谢!同时大家有什么关于锁的问题可以留言,一起交流,共同进步~??祝大家进步e非常一天一点点~