在平时开发中我们经常会使用多线程,多线程为我们带来了很大便利,也提高了程序的执行效率,但同时也带来了Data raceData race的定义很简单:当至少有两个线程同时访问同一个变量,而且至少其中有一个是写操作时,就发生了Data race。所以这是就要利用一些同步机制来确保数据的准确性,锁就是同步机制中的一种。

怎么检测项目中的Data race

只需要在设置中勾选Thread Sanitizer 即可,顺便可以勾选Pause on issues 就可以断点到相应的代码。 更多延伸内容请参考Peak君的如何用Xcode8解决多线程问题和iOS多线程到底不安全在哪里?

下面就今日正题简单聊一聊iOS中的锁,以及相关的内容(由于本人能力有限,文中难免有一些遗漏或者错误,请各位看官不吝赐教!谢谢!?)

简单的性能测试

下图是我针对iOS中的锁自己测试得出的,图中数字代表每次加解锁需要消耗的时间,单位为ns。代码在这里,代码参考自YY大神的不再安全的 OSSpinLock,基本跟YY大神的图差不多?,YY大神的单位是μs,应该是1000次的,或者写错了吧~

  • 注:运行手机: iphone6s plus ,系统版本:11.2.2,Xcode9.2;数字的单位为ns(得出的具体数值是跑了多次取的均值)。

值得注意的是:1.这个数字仅仅代表每次加解锁的耗时,并不能全方面的代表性能。2.不同的机型和系统,不同的循环次数可能结果会略微有些差异。 但是还是可以看出@synchronized:是表现最差的。

在具体说这些锁之前,先来说几个概念定义:(参考维基百科)

  1. 临界区: 指的是一块对公共资源进行访问的代码,并非一种机制或是算法。

  2. 自旋锁: 是用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。 自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。

  3. 互斥锁(Mutex): 是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。

  4. 读写锁: 是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁) 用于解决多线程对公共资源读写问题。读操作可并发重入,写操作是互斥的。 读写锁通常用互斥锁、条件变量、信号量实现。

  5. 信号量(semaphore): 是一种更高级的同步机制,互斥锁可以说是semaphore在仅取值0/1时的特例。信号量可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。

  6. 条件锁: 就是条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。

互斥锁

1.NSLock: 是Foundation框架中以对象形式暴露给开发者的一种锁,(Foundation框架同时提供了NSConditionLockNSRecursiveLockNSConditionNSLock定义如下:

@protocol NSLocking- (void)lock;
- (void)unlock;@end@interface NSLock : NSObject <NSLocking> {
@privatevoid *_priv;
}- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));@end
复制代码

tryLock 和 lock 方法都会请求加锁,唯一不同的是trylock在没有获得锁的时候可以继续做一些任务和处理。lockBeforeDate方法也比较简单,就是在limit时间点之前获得锁,没有拿到返回NO。 **实际项目中:**NSLock在AFNetworking的AFURLSessionManager.m中应用如下:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {...self.lock = [[NSLock alloc] init];self.lock.name = AFURLSessionManagerLockName;...
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegateforTask:(NSURLSessionTask *)task
{...[self.lock lock];self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;[delegate setupProgressForTask:task];[self addNotificationObserverForTask:task];[self.lock unlock];
}
复制代码

2.pthread_mutex: 实际项目中: 在YYKit的YYMemoryCach中可以看到

- (instancetype)init {...pthread_mutex_init(&_lock, NULL);...
}
- (void)_trimToCost:(NSUInteger)costLimit {BOOL finish = NO;pthread_mutex_lock(&_lock);if (costLimit == 0) {[_lru removeAll];finish = YES;} else if (_lru->_totalCost <= costLimit) {finish = YES;}pthread_mutex_unlock(&_lock);if (finish) return;NSMutableArray *holder = [NSMutableArray new];while (!finish) {if (pthread_mutex_trylock(&_lock) == 0) {if (_lru->_totalCost > costLimit) {_YYLinkedMapNode *node = [_lru removeTailNode];if (node) [holder addObject:node];} else {finish = YES;}pthread_mutex_unlock(&_lock);} else {usleep(10 * 1000); //10 ms}}...
}
复制代码

3.@synchronized:

实际项目中: AFNetworking中 isNetworkActivityOccurring属性的getter方法

- (BOOL)isNetworkActivityOccurring {@synchronized(self) {return self.activityCount > 0;}
}
复制代码

关于 @synchronized推荐扩展阅读 关于 @synchronized,这儿比你想知道的还要多

自旋锁

1.OSSpinLock:

OSSpinLock lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&lock);
...
OSSpinLockUnlock(&lock);
复制代码

上面是OSSpinLock使用方式,编译会报警告,已经废弃了,OSSpinLock大家也已经不再用它了,因为它在某一些场景下已经不安全了,可以参考 YY大神的不再安全的 OSSpinLock,在Protocol Buffers项目中你可以看到这样的注释,大家已经用新的方案替换了。

 // NOTE: OSSpinLock may seem like a good fit here but Apple engineers have// pointed out that they are vulnerable to live locking on iOS in cases of// priority inversion://   http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe///   https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
复制代码

2.os_unfair_lock: os_unfair_lock 是苹果官方推荐的替换OSSpinLock的方案,但是它在iOS10.0以上的系统才可以调用。

os_unfair_lock_t unfairLock;
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 = [NSRecursiveLock new];
- (void)dealloc {[_lock lock];......[_lock unlock];
}
复制代码

2.pthread_mutex(recursive): pthread_mutex锁也支持递归,只需要设置PTHREAD_MUTEX_RECURSIVE即可

pthread_mutex_t lock;
pthread_mutexattr_t attr;
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);
复制代码

条件锁

1. NSCondition: 定义:

@interface NSCondition : NSObject <NSLocking> {
@privatevoid *_priv;
}- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
复制代码

遵循NSLocking协议,使用的时候同样是lock,unlock加解锁,wait是傻等,waitUntilDate:方法是等一会,都会阻塞掉线程,signal是唤起一个在等待的线程,broadcast是广播全部唤起。

NSCondition *lock = [[NSCondition alloc] init];
//Son 线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[lock lock];while (No Money) {[lock wait];}NSLog(@"The money has been used up.");[lock unlock];
});//Father线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{[lock lock];NSLog(@"Work hard to make money.");[lock signal];[lock unlock];});
复制代码

2.NSConditionLock: 定义:

@interface NSConditionLock : NSObject <NSLocking> {
@privatevoid *_priv;
}- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
复制代码

很简单,方法很清晰,基本同上。

信号量

dispatch_semaphore: dispatch_semaphore在YYKit中的YYThreadSafeArray.m有所应用,YY大神有这样一句注释:

@discussion Generally, access performance is lower than NSMutableArray, but higher than using @synchronized, NSLock, or pthread_mutex_t.
复制代码
#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \
__VA_ARGS__; \
dispatch_semaphore_signal(_lock);
复制代码

总结:

其实本文写的都是一些再基础不过的内容,在平时阅读一些开源项目的时候经常会遇到一些保持线程同步的方式,因为场景不同可能选型不同,这篇就做一下简单的记录吧~我相信读完这篇你应该能根据不同场景选择合适的锁了吧、能够道出自旋锁互斥锁的区别了吧。

最后:

由于本人能力有限,文中难免有一些遗漏或者错误,请各位看官不吝赐教!谢谢!同时有任何关于锁相关的疑问可以疯狂留言,一起交流,一起进步~? 祝大家每天都能进步一点点~

扩展阅读:

  1. 不再安全的 OSSpinLock ??
  2. 深入理解 iOS 开发中的锁 ???
  3. 关于 @synchronized,这儿比你想知道的还要多 ???
  4. pthread的各种同步机制- Casa Taloyum ??
  5. Threading Programming Guide

iOS开发中的11种锁以及性能对比相关推荐

  1. 最新 iOS开发中的11种锁以及性能对比

    在平时开发中我们经常会使用多线程,多线程为我们带来了很大便利,也提高了程序的执行效率,但同时也带来了Data race,Data race的定义很简单:当至少有两个线程同时访问同一个变量,而且至少其中 ...

  2. iOS开发中自旋和互斥锁的理解以及所有锁的性能比较

    补充: 可以看到除了 OSSpinLock 外,dispatch_semaphore 和 pthread_mutex 性能是最高的.苹果在新系统中已经优化了 pthread_mutex 的性能,所以它 ...

  3. IOS开发中的几种设计模式

    ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现. 优势:解耦合 敏捷 ...

  4. 如何深入理解 iOS 开发中的锁?

    摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不 ...

  5. 理解:iOS开发中锁的实现原理

    摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不 ...

  6. 深入理解 iOS 开发中的锁

    深入理解 iOS 开发中的锁 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知 ...

  7. iOS开发中的锁实现猜测

    本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不会详细 ...

  8. iOS开发中拉伸图片的几种方式

    在iOS开发中,经常会遇到控件尺寸和图片大小不匹配的情况. 一些情况下, 我们需要对图片进行拉伸, 以满足美观需求. 总的来说, 图片的拉伸方式可以分为两种, 一种是通过Xcode自带的Show Sl ...

  9. iOS开发UI篇—iOS开发中三种简单的动画设置

    [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要"参与到"动画中 [UIView beginAnimations: ...

  10. iOS 开发中的多线程

    线程.进程 什么是线程.进程   有的人说进程就像是人的脑袋,线程就是脑袋上的头发~~.其实这么比方不算错,但是更简单的来说,用迅雷下载文件,迅雷这个程序就是一个进程,下载的文件就是一个线程,同时下载 ...

最新文章

  1. 数值分析龙贝格matlab,龙贝格matlab程序
  2. vs2022 无法打开包括文件: “crtdbg.h” no such file or directory
  3. haproxy代理hive
  4. mysql列调换位置_mysql互换表中两列数据方法
  5. 经验 | 上交机械本硕转计算机视觉岗位面经
  6. 在程序设计中使用Interface
  7. 反射parameter field_Java反射有多强?这5大神奇功能,你需要了解!
  8. 美团点评架构再调整,王兴凭什么同时杠上阿里滴滴饿了么
  9. Java-IO操作性能对比
  10. echarts中国地图下钻到区
  11. 异速联客户端未获取到服务器信息,异速联客户端登陆服务器地址
  12. H3C Comware V3 端口聚合
  13. word前两页不设置页码,从第三页开始设置页码(word页码设置)
  14. LimeSDR实验教程(14) GSM嗅探
  15. 根据excel模板导出多sheet且生成条形码或二维码插入excel指定位置中
  16. c语言求众数,众数求c++程序
  17. php+bmp+加密,郁闷啊,谁知道BMP图片加密技术吗
  18. blender2.8设置玻璃材质
  19. 关于微信公众号文章编辑器不能直接编辑html样式的处理方法
  20. Notepad++的JsonViewer 插件安装失败的解决

热门文章

  1. 面向对象六大原则----imageLoader为例层层优化
  2. 我给曾经暗恋的高中女同学,用Python实现了她飞机上刷抖音
  3. python基础篇--变量和简单的数据类型(中)
  4. echarts大屏模板_大屏数据展示模板智慧城市
  5. python时间处理方法_基于python时间处理方法(详解)
  6. 保护计算机系统与数据有什么方法,电脑数据保护方法 看完保你不后悔
  7. 用命令行查看mysql,利用命令行查看Mysql数据库
  8. java类似keyvaluepair_BM25 算法的java实现,有详细的说明文档和代码 Develop 238万源代码下载- www.pudn.com...
  9. java hql 连接查询,java – 如何从HQL表单中的两个连接表查询中选择*?
  10. 学生选课系统代码-4c【interface】视图层代码【MVC--c】代码