iOS开发之用到的几种锁整理
1. iOS中的互斥锁
在编程中,引入对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问对象。
1.1 @synchronized (self)
- (void)lock1 {
@synchronized (self) {
// 加锁操作
}
}
1.2 NSLock
- (void)lock2 {
NSLock *xwlock = [[NSLock alloc] init];
XWLogBlock logBlock = ^ (NSArray *array) {
[xwlock lock];
for (id obj in array) {
NSLog(@"%@",obj);
}
[xwlock unlock];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *array = @[@1,@2,@3];
logBlock(array);
});
}
1.3 pthread
pthread除了创建互斥锁,还可以创建递归锁、读写锁、once等锁
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"+++++ 线程1 start");
pthread_mutex_lock(&mutex);
sleep(2);
pthread_mutex_unlock(&mutex);
NSLog(@"+++++ 线程1 end");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"----- 线程2 start");
pthread_mutex_lock(&mutex);
sleep(3);
pthread_mutex_unlock(&mutex);
NSLog(@"----- 线程2 end");
});
}
2. iOS中的递归锁
同一个线程可以多次加锁,不会造成死锁
死锁->
- (void)lock5 {
NSLock *commonLock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^XWRecursiveBlock)(int);
XWRecursiveBlock = ^(int value) {
[commonLock lock];
if (value > 0) {
NSLog(@"加锁层数: %d",value);
sleep(1);
XWRecursiveBlock(--value);
}
NSLog(@"程序退出!");
[commonLock unlock];
};
XWRecursiveBlock(3);
});
}
<-死锁
2.1 NSRecursiveLock
- (void)lock4 {
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^XWRecursiveBlock)(int);
XWRecursiveBlock = ^(int value) {
[recursiveLock lock];
if (value > 0) {
NSLog(@"加锁层数: %d",value);
sleep(1);
XWRecursiveBlock(--value);
}
NSLog(@"程序退出!");
[recursiveLock unlock];
};
XWRecursiveBlock(3);
});
}
2.2 pthread
- (void)lock6 {
__block pthread_mutex_t recursiveMutex;
pthread_mutexattr_t recursiveMutexattr;
pthread_mutexattr_init(&recursiveMutexattr);
pthread_mutexattr_settype(&recursiveMutexattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&recursiveMutex, &recursiveMutexattr);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^XWRecursiveBlock)(int);
XWRecursiveBlock = ^(int value) {
pthread_mutex_lock(&recursiveMutex);
if (value > 0) {
NSLog(@"加锁层数: %d",value);
sleep(1);
XWRecursiveBlock(--value);
}
NSLog(@"程序退出!");
pthread_mutex_unlock(&recursiveMutex);
};
XWRecursiveBlock(3);
});
}
3. 信号量
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量
3.1 dispatch_semaphore_t
实现 GCD 下同步
- (void)lock7 {
// dispatch_semaphore_create //创建一个信号量 semaphore
// dispatch_semaphore_signal //发送一个信号 信号量+1
// dispatch_semaphore_wait // 等待信号 信号量-1
/// 需求: 异步线程的两个操作同步执行
dispatch_semaphore_t semaphone = dispatch_semaphore_create(0);
NSLog(@"start");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"async .... ");
sleep(5);
/// 线程资源 + 1
dispatch_semaphore_signal(semaphone);//信号量+1
});
/// 当前线程资源数量为 0 ,等待激活
dispatch_semaphore_wait(semaphone, DISPATCH_TIME_FOREVER);
NSLog(@"end");
}
3.2 pthread
- (void)lock8 {
__block pthread_mutex_t semaphore = PTHREAD_MUTEX_INITIALIZER;
__block pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
NSLog(@"start");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&semaphore);
NSLog(@"async...");
sleep(5);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&semaphore);
});
pthread_cond_wait(&cond, &semaphore);
NSLog(@"end");
}
4. 条件锁
4.1 NSCondition
NSCondition 的对象实际上是作为一个锁和线程检查器,锁主要是为了检测条件时保护数据源,执行条件引发的任务。线程检查器主要是根据条件决定是否继续运行线程,即线程是否被阻塞。
NSCondition同样实现了NSLocking协议,所以它和NSLock一样,也有NSLocking协议的lock和unlock方法,可以当做NSLock来使用解决线程同步问题,用法完全一样。
- (NSMutableArray *)removeLastImage:(NSMutableArray *)images {
if (images.count > 0) {
NSCondition *condition = [[NSCondition alloc] init];
[condition lock];
[images removeLastObject];
[condition unlock];
NSLog(@"removeLastImage %@",images);
return images;
}else{
return NULL;
}
}
同时,NSCondition提供更高级的用法。wait和signal,和条件信号量类似。
NSCondition和NSLock、@synchronized等是不同的是,NSCondition可以给每个线程分别加锁,加锁后不影响其他线程进入临界区。这是非常强大。
- (void)lock10 {
self.conditionArray = [NSMutableArray array];
self.xwCondition = [[NSCondition alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.xwCondition lock];
if (self.conditionArray.count == 0) {
NSLog(@"等待制作数组");
[self.xwCondition wait];
}
id obj = self.conditionArray[0];
NSLog(@"获取对象进行操作:%@",obj);
[self.xwCondition unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.xwCondition lock];
id obj = @"极客学伟";
[self.conditionArray addObject:obj];
NSLog(@"创建了一个对象:%@",obj);
[self.xwCondition signal];
[self.xwCondition unlock];
});
}
4.2 NSConditionLock
- (void)lock11 {
NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
NSMutableArray *arrayM = [NSMutableArray array];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[conditionLock lock];
for (int i = 0; i < 6; i++) {
[arrayM addObject:@(i)];
NSLog(@"异步下载第 %d 张图片",i);
sleep(1);
if (arrayM.count == 4) {
[conditionLock unlockWithCondition:4];
}
}
});
dispatch_async(dispatch_get_main_queue(), ^{
[conditionLock lockWhenCondition:4];
NSLog(@"已经获取到4张图片->主线程渲染");
[conditionLock unlock];
});
}
4.3 pthread POSIX Conditions
- (void)lock12 {
__block pthread_mutex_t mutex;
__block pthread_cond_t condition;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&condition, NULL);
NSMutableArray *arrayM = [NSMutableArray array];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
for (int i = 0; i < 6; i++) {
[arrayM addObject:@(i)];
NSLog(@"异步下载第 %d 张图片",i);
sleep(1);
if (arrayM.count == 4) {
pthread_cond_signal(&condition);
}
}
});
dispatch_async(dispatch_get_main_queue(), ^{
pthread_cond_wait(&condition, &mutex);
NSLog(@"已经获取到4张图片->主线程渲染");
pthread_mutex_unlock(&mutex);
});
}
5. 读写锁
读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
5.1 dispatch_barrier_async / dispatch_barrier_sync
有一个需求,如图:
任务1,2,3 均执行完毕执行任务0,然后执行任务4,5,6.
- (void)lock13 {
dispatch_queue_t queue = dispatch_queue_create("com.qiuxuewei.brrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务1 -- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2 -- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3 -- %@",[NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@"任务0 -- %@",[NSThread currentThread]);
for (int i = 0; i < 100; i++) {
if (i % 30 == 0) {
NSLog(@"任务0 --- log:%d -- %@",i,[NSThread currentThread]);
}
}
});
NSLog(@"dispatch_barrier_sync down!!!");
dispatch_async(queue, ^{
NSLog(@"任务4 -- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务5 -- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务6 -- %@",[NSThread currentThread]);
});
}
dispatch_barrier_async 和 dispatch_barrier_sync 的异同
共同点
等待它前面的执行完才执行自己的任务
等待自己任务执行结束才执行后面的任务
不同点
dispatch_barrier_async 将自己的任务插入到队列之后会继续将后面的操作插入到队列,按照规则先执行前面队列的任务,等自己队列执行完毕,再执行后面队列的任务
dispatch_barrier_sync 将自己的任务插入到队列之后,先等待自己的任务执行完毕才会执行后面操作插入到队列,再执行后面队列的任务。
5.2 pthread
- (void)lock14 {
__block pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
__block NSMutableArray *arrayM = [NSMutableArray array];
XWBlock writeBlock = ^ (NSString *str) {
NSLog(@"开启写操作");
pthread_rwlock_wrlock(&rwlock);
[arrayM addObject:str];
sleep(2);
pthread_rwlock_unlock(&rwlock);
};
XWVoidBlock readBlock = ^ {
NSLog(@"开启读操作");
pthread_rwlock_rdlock(&rwlock);
sleep(1);
NSLog(@"读取数据:%@",arrayM);
pthread_rwlock_unlock(&rwlock);
};
for (int i = 0; i < 5; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
writeBlock([NSString stringWithFormat:@"%d",i]);
});
}
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
readBlock();
});
}
}
6.自旋锁
bool lock = false; // 一开始没有锁上,任何线程都可以申请锁
do {
while(lock); // 如果 lock 为 true 就一直死循环,相当于申请锁
lock = true; // 挂上锁,这样别的线程就无法获得锁
Critical section // 临界区
lock = false; // 相当于释放锁,这样别的线程可以进入临界区
Reminder section // 不需要锁保护的代码
}
6.1 OSSpinLock
YYKit作者的文章 不再安全的 OSSpinLock有说到这个自旋锁存在优先级反转的问题。
6.2 os_unfair_lock
自旋锁已经不再安全,然后苹果又整出来个 os_unfair_lock_t ,这个锁解决了优先级反转的问题。
os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock);
7. property - atomic / nonatomic
atomic 修饰的对象,系统会保证在其自动生成的 getter/setter 方法中的操作是完整的,不受其他线程的影响。例如 A 线程在执行 getter 方法时,B线程执行了 setter 方法,此时 A 线程依然会得到一个完整无损的对象。
atomic
默认修饰符
会保证CPU能在别的线程访问这个属性之前先执行完当前操作
读写速度慢
线程不安全 - 如果有另一个线程 D 同时在调[name release],那可能就会crash,因为 release 不受 getter/setter 操作的限制。也就是说,这个属性只能说是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。
nonatomic
不默认
速度更快
线程不安全
如果两个线程同时访问会出现不可预料的结果。
8. Once 原子操作
8.1 GCD
- (id)lock15 {
static id shareInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!shareInstance) {
shareInstance = [[NSObject alloc] init];
}
});
return shareInstance;
}
8.2 pthread
- (void)lock16 {
pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_once(&once, lock16Func);
}
void lock16Func() {
static id shareInstance;
shareInstance = [[NSObject alloc] init];
}
iOS开发之用到的几种锁整理的更多相关文章
- iOS开发之动画编程的几种方法
iOS开发之动画编程的几种方法 IOS中的动画总结来说有五种:UIView<block>,CAAnimation<CABasicAnimation,CATransition,CAKe ...
- IOS开发中数据持久化的几种方法--NSUserDefaults
IOS开发中数据持久化的几种方法--NSUserDefaults IOS 开发中,经常会遇到需要把一些数据保存在本地的情况,那么这个时候我们有以下几种可以选择的方案: 一.使用NSUserDefaul ...
- iOS开发 跳转场景的三种方式
iOS开发 跳转场景的三种方式 2012年10月17日, 15:32 假设A跳转到B,三种方法:1.按住ctrl键,拖动A上的控件(比如说UIButton)到B上,弹出菜单,选择Modal.不需要写任 ...
- iOS开发控制器之间传值的几种小方法
在IOS开发中或面试中,经常会遇到,两个或者多个控制器之间传值的问题 ,总结的集中方法仅供参考! 问题 :将B控制器中的textField 输入内容,传到A控制器中的label上显示出来,如何传值? ...
- iOS开发进阶-实现多线程的3种方法
相关文章链接: 1.多线程简介 2.实现多线程的3种方法 ......待续 前言 在多线程简介中,我已经说明过了,为了提高界面的流畅度以及用户体验.我们务必要把耗时的操作放到别的线程中去执行,千万不要 ...
- iOS开发中数组常用的五种遍历方式
随着iOS的不断发展,apple也不断推出性能更高的数组遍历方式,下面将对熟悉的五种遍历方式进行列举. 首先定义一个数组,并获取数组长度 NSArray *array=@[",]; NSIn ...
- iOS开发中关于nslog的几种流行做法小结
不管哪种方法,都必须在PCH文件中做下宏定义 DEBUG和RELEASE要分开,RELEASE时log打印要取消 方法一:简单直接,用几行代码搞定,简洁但功能少 #ifdef DEBUG #defin ...
- IOS开发之自定义Button(集成三种回调模式)
前面在做东西的时候都用到了storyboard,在今天的代码中就纯手写代码自己用封装个Button.这个Button继承于UIView类,在封装的时候用上啦OC中的三种回调模式:目标动作回调,委托回调 ...
- 【iOS开发-30】UITabBarController的几种代理方法以及结合NSUserDefaults还原上次退出时被选中视图控制器和视图控制器的顺序
一.UITabBarController的几种代理方法 在AppDelegate.h中加入一个协议<UITabBarControllerDelegate>.然后再AppDelegate.m ...
随机推荐
- java 虚拟机内存模型
[声明] 欢迎转载,但请保留文章原始出处→_→ 文章来源:[http://www.cnblogs.com/smyhvae/p/4748392.html] 文章来源:[http://www.cnblog ...
- mysql 的存储引擎介绍
在数据库中存的就是一张张有着千丝万缕关系的表,所以表设计的好坏,将直接影响着整个数据库.而在设计表的时候,我们都会关注一个问题,使用什么存储引擎.等一下,存储引擎?什么是存储引擎? 什么是存储引擎? ...
- vue路由嵌套,vue動態路由
https://www.cnblogs.com/null11/p/7486735.html https://www.cnblogs.com/goloving/p/9271501.html https: ...
- git 操作命令详解
git 什么是git 开源的分布式版本控制系统, 用于高效的管理大小项目和文件 代码管理工具 防止代码丢失, 做备份 代码版本管控, 设置节点, 多版本切换 建立分支各自开发, 互不影响, 方便合并 ...
- MT【315】勾股数
(高考压轴题)证明以下命题:(1)对任意正整数$a$都存在正整数$b,c(b<c)$,使得$a^2,b^2,c^2$成等差数列.(2)存在无穷多个互不相似的三角形$\Delta_n$,其边长$a ...
- 【BZOJ5502】[GXOI/GZOI2019]与或和(单调栈)
[BZOJ5502][GXOI/GZOI2019]与或和(单调栈) 题面 BZOJ 洛谷 题解 看到位运算就直接拆位,于是问题变成了求有多少个全\(0\)子矩阵和有多少个全\(1\)子矩阵. 这两个操 ...
- pytest 9 pytest-datadir读取文件信息
安装:pip install pytest-datadir 介绍:用于操作测试数据目录和文件的插件.pytest-datadir他会寻找包含测试模块名字的文件夹或者全局的一个文件夹名字为data下的数 ...
- Springboot 3.需求携带参数的get请求
还是拿来上节讲的代码: package com.course.server; import org.springframework.web.bind.annotation.*; import java ...
- 浏览器开发者工具----F12 功能介绍
笔者技巧: 看了些其它回答,有些是用来扒图片的,有些是写爬虫的(这个不要看Elements,因为浏览器会对一些不符合规范的标签做补全或者其它处理,最好是Ctrl+U). 图片的话就不要看Network ...
- Linux系统诊断必备技能之二:tcpdump抓包工具详解
一.简述 TcpDump可以将网络中传送的数据包完全截获下来提供分析.它支持针对网络层.协议.主机.网络或端口的过滤,并提供and.or.not等逻辑语句来帮助你去掉无用的信息. Linux作为网络服 ...