上篇文章讲了一下NSThread的基本操作,本篇讲一下NSThread的一些高级用法。

线程间资源共享&线程加锁

在程序运行过程中,如果存在多线程,呢么各个线程读写资源就会存在先后、同时读写资源的操作,因为实在不同线程中,CPU调度过程中我们无法保证哪个线程会先读写资源,哪个线程后读写资源。这就有可能操作数据混乱和错误。因此为了防止数据读写混乱和错误的发生,我们要将线程在读写数据时加锁,这样就能保证操作同一个数据UI小的线程只有一个,当这个线程执行完成之后解锁,其他的线程才能操作此数据对象。NSLock/NSConditiongLock/NSRecursivelovk/@synchronized都可以实现线程上锁的操作。

1.@synchronized

直接上例子:12306抢火车票

// 首先:开启两个线程同时售票
self.tickets = 20;
NSThread *t1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
t1.name = @"售票员A";
[t1 start]; NSThread *t2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
t2.name = @"售票员B";
[t2 start]; // 然后:将售票的方法加锁
- (void)saleTickets{
while (YES) {
[NSThread sleepForTimeInterval:1.0];
//互斥锁 -- 保证锁内的代码在同一时间内只有一个线程在执行
@synchronized (self){
//1.判断是否有票
if (self.tickets > 0) {
//2.如果有就卖一张
self.tickets --;
NSLog(@"还剩%d张票 %@",self.tickets,[NSThread currentThread]);
}else{
//3.没有票了提示
NSLog(@"卖完了 %@",[NSThread currentThread]);
break;
}
}
} }

2.NSLock

-(BOOL)tryLock;//尝试加锁,成功返回YES ;失败返回NO ,但不会阻塞线程的运行
-(BOOL)lockBeforeDate:(NSDate *)limit;//在指定的时间以前得到锁。YES:在指定时间之前获得了锁;NO:在指定时间之前没有获得锁。该线程将被阻塞,直到获得了锁,或者指定时间过期。
- (void)setName:(NSString*)newName//为锁指定一个Name
- (NSString*)name//**返回锁指定的**name
@property (nullable, copy) NSString *name;线程锁名称

举个例子

NSLock *myLock = [[NSLock alloc] init];
static NSString *str = @"hello";
[NSThread detachNewThreadWithBlock:^{
NSLog(@"%d",[myLock tryLock]);
[myLock lock];
NSLog(@"%d",[myLock tryLock]);
NSLog(@"%@",str);
str = @"123";
[myLock unlock];
}]; [NSThread detachNewThreadWithBlock:^{
[myLock lock];
NSLog(@"%@",str);
str = @"11111";
[myLock unlock];
}];

输出结果不加锁之前,两个线程输出一样都是hello;枷锁之后,输出就会改变hello和123.

3.NSConditionLock

使用此锁,在线程没有获得锁的情况下,阻塞,即暂停运行,典型用于生产者/消费者模型。

- (instancetype)initWithCondition:(NSInteger)condition;//初始化条件锁
- (void)lockWhenCondition:(NSInteger)condition;//加锁 (条件是:锁空闲,即没被占用;条件成立)
- (BOOL)tryLock; //尝试加锁,成功返回TRUE,失败返回FALSE
- (BOOL)tryLockWhenCondition:(NSInteger)condition;//在指定条件成立的情况下尝试加锁,成功返回TRUE,失败返回FALSE
- (void)unlockWithCondition:(NSInteger)condition;//在指定的条件成立时,解锁
- (BOOL)lockBeforeDate:(NSDate *)limit;//在指定时间前加锁,成功返回TRUE,失败返回FALSE,
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//条件成立的情况下,在指定时间前加锁,成功返回TRUE,失败返回FALSE,
@property (readonly) NSInteger condition;//条件锁的条件
@property (nullable, copy) NSString *name;//条件锁的名称

举个例子:

NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
[NSThread detachNewThreadWithBlock:^{
for (int i = 0; i < 5; i++) {
[conditionLock lock];
NSLog(@"当前解锁条件:%d",i);
sleep(2);
[conditionLock unlockWithCondition:i];
BOOL isLocked = [conditionLock tryLockWhenCondition:2];
if (isLocked) {
NSLog(@"%d加锁成功!!!!!",i);
[conditionLock unlock];
} else {
NSLog(@"%d加锁失败!!!!!",i);
} }
}];

输出结果:

4.NSRecursiveLock

此锁可再同一线程中多次被使用,但要保证加锁和解锁使用平衡,多用于递归函数,防止死锁。

- (BOOL)tryLock;//尝试加锁,成功返回TRUE,失败返回FALSE
- (BOOL)lockBeforeDate:(NSDate *)limit;//在指定时间前尝试加锁,成功返回TRUE,失败返回FALSE
@property (nullable, copy) NSString *name;//线程锁名称

举个例子:

-(void)initRecycle:(int)value
{
[self.myRecursive lock];
if(value>0)
{
NSLog(@"当前的value值:%d",value);
sleep(2);
[self initRecycle:value-1];
}
[self.myRecursive unlock];
}

线性安全之原子属性atomic

原子属性(线程安全)与非原子属性,是什么意思呢?

苹果系统在我们声明对象属性时默认是atomic,也就是在读写这个属性时,保证同一时间内只有一个线程能都执行,当声明时用的是atomic,通常会生成_成员变量,如果同时重写了getter&setter方法,_a成员变量就不自动生成。实际上原子属性内部有一个锁,叫做自旋锁。

自旋锁和互斥锁

共同点
都能够保证线程安全
不同点
互斥锁:如果其他线程正在执行锁定的代码,此线程就会进入休眠状态,等待锁打开;然后被唤醒
自旋锁:如果线程被锁在外面,那么就会用死循环的方式一直等待锁打开! 自旋锁是一种互斥锁的实现方式,相比较一般的互斥锁会在等待期间放弃cpu,自旋锁则是不断循环并测试锁的状态,会一直占用cpu。 互斥锁:用于保护临界区,确保同一时间只有一个线程访问数据。对共享资源的访问,先对互斥量进行加锁,如果互斥量已经上锁,调用线程会阻塞,直到互斥量被解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
临界区:每个进程中访问临界资源的那段程序称为临界区,每次只允许一个进程进入临界区,进入后不允许其他进程进入。
自旋锁:与互斥量类似,它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态。用在以下情况:锁持有的时间短,而且线程并不希望在重新调度上花太多的成本。"原地打转"。 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

无论什么锁,都很消耗性能,效率不高,所以在我们平时开发过程中,会使用nonatomic。

举个例子

@property (strong, nonatomic) NSObject *myNonatomic;
@property (strong, atomic) NSObject *myAtomic; // 当我们重写了myAtomic的setter和getter方法时
- (void)setMyAtomic:(NSObject *)myAtomic{
_myAtomic = myAtomic;
}
- (NSObject *)myAtomic{
return _myAtomic;
} // 那么我们就必须声明一个_myAtomic静态变量
@synthesize myAtomic = _myAtomic;
// 否则系统在编译的时候找不到 _myAtomic

子线程的Runloop

在介绍子线程上的Runloop之前先来一个有意思的小插曲,我们来介绍一下Runloop,甚至模拟一个Runloop

Runloop 运行循环
-在目前iOS开发中,几乎用不到,在以前iOS黑暗时代,程序员会用到
目的:
保证程序不退出
监听事件
没有事件让程序进入休眠
区分模式:
NSDefaultRunLoopMode - 时钟、网络事件
NSRunLoopCommonModes - 用户交互

// 模拟runloop
void click(int type){
printf("正在运行第%d",type);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
while (YES) {
printf("请输入选项 0 表示退出");
int result = -1;
scanf("%d",&result);
if (result == 0) {
printf("程序结束\n");
break;
}else{
click(result);
}
}
}
return 0;
}
在iOS中,开辟的子线程上的Runloop是默认不开启的,并且子线程中的Runloop开启之后是手动无法关闭的。那么当我们给子线程中重复添加不同任务时并且Runloop没有开启的情况下,子线程无法监听事件(确切说是子线程的Runloop),我们后来添加的任务就无法执行。
但是我们如果让子线程Runloop一直工作又浪费资源,下面介绍一个OC中常用到的可以控制子线程Runloop的例子:
首先,Runloop就是一个死循环,那么我们就创建一个死循环,然后声明一个可以判断是否应该退出Runloop循环的属性
@property (assign, nonatomic, getter=isFinished) BOOL finished;

// 创建子线程并添加任务
NSThread *t = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
[t start];
self.finished = NO;
[self performSelector:@selector(otherMethod) onThread:t withObject:nil waitUntilDone:NO]; // 在第一个任务中加入死循环
- (void)demo{
NSLog(@"%@",[NSThread currentThread]);
//在OC中使用比较多的,退出循环的方式
while (!self.isFinished) {
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
}
NSLog(@"能来吗?");
} // 在最后添加的任务结束后结束死循环
- (void)otherMethod{
for (int i = 0; i < 10; i ++) {
NSLog(@"%s %@",__FUNCTION__,[NSThread currentThread]); }
//让上面方法中的死循环结束
self.finished = YES;
}

【iOS】从实际出发理解多线程(二)--NSThread高级操作的更多相关文章

  1. iOS 从实际出发理解多线程

    前言 多线程很多开发者多多少少相信也都有了解,以前有些东西理解的不是很透,慢慢的积累之后,这方面的东西也需要自己好好的总结一下.多线程从我刚接触到iOS的时候就知道这玩意挺重要的,但那时也是能力有限, ...

  2. iOS开发之多线程(NSThread、NSOperation、GCD)

    整理一些多线程相关的知识. 并行 & 并发 1.并行:并行是相对于多核而言的,几个任务同时执行.2.并发:并发是相对于单核而言的,几个任务之间快速切换运行,看起来像是"同时" ...

  3. iOS开发之再探多线程编程:Grand Central Dispatch详解

    Swift3.0相关代码已在github上更新.之前关于iOS开发多线程的内容发布过一篇博客,其中介绍了NSThread.操作队列以及GCD,介绍的不够深入.今天就以GCD为主题来全面的总结一下GCD ...

  4. iOS知识点、面试题 之二

    最近面试,与大家分享一下,分三文给大家: 当然Xcode新版本区别,以及iOS新特性 Xcode8 和iOS 10 在之前文章有发过,感兴趣的可以查阅: http://www.cnblogs.com/ ...

  5. iOS学习——(转)多线程

    转载自:iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用 一.多线程的基本概念 进程:可以理解成一个运行中的应用程序,是系统进行资源分配和 ...

  6. iOS开发Swift篇(02) NSThread线程相关简单说明

    iOS开发Swift篇(02) NSThread线程相关简单说明 一 说明 1)关于多线程部分的理论知识和OC实现,在之前的博文中已经写明,所以这里不再说明. 2)该文仅仅简单讲解NSThread在s ...

  7. iOS 面试常问之多线程

    本片围绕多线程全面展开叙述. 1.为什么要有多线程/多线程是用来干什么的? 2.多线程是什么? 3.如何创建多线程? 4.多线程在哪些情况下会使用/多线程使用场景? 5.三种多线程的优缺点? 6.线程 ...

  8. C#夯实基础之多线程二:主线程、前台线程与后台线程

    我们在<C#夯实基础之多线程一:初识多线程>一文中第二部分中指出,既然windows最终发展出了多线程模型,按理说,我们直接使用一个.NetFramework的线程类就可以直接撸代码了,但 ...

  9. Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理

    Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理 作者:宋宸宁(20135315) 一.实验过程 图1 编写songchenning5315.c文件 图2 将c文件汇编成32位机器语言 ...

  10. Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理

    Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理 计算机工作原理 汇编指令 C语言代码汇编分析 by苏正生 原创作品转载请注明出处 <Linux内核分析>MOOC课程htt ...

随机推荐

  1. java--http协议

    web应用目录结构 |- WebRoot 根目录 |-静态资源(html+css+javascript+images+xml) 可以直接被浏览器访问到的 |-WEB-INF 不可以直接被浏览器访问到 ...

  2. 前端开发系列125-进阶篇之Iterator

    本文简单说明[ 迭代器接口 Iterator]() 接口的基本使用,涉及 Array .Set .Map 和 String 以及伪数组等数据结构,以及 `for...of`循环的用法等. Iterat ...

  3. linux 网络编程 新技能

    {} 配对问题.这个在多个json文件中找到配对的{} 括号实在不容易. 使用 % 号吧少年. gg 跳到首部 GG 跳转到尾部. XXG 可以跳转到多少多少行. gcc -E 执行预处理的结果.变量 ...

  4. audio 定制化

    简介 RT 参考连接 https://www.cnblogs.com/lalalagq/p/9961959.html

  5. 在服务器上安装 gitlab

    简介 RT 核心参考链接 https://about.gitlab.com/install/#ubuntu?version=ce https://www.cnblogs.com/zhujingzhi/ ...

  6. 线性代数 A 的 LU 分解

    我们本章的目的是对 \(A=LU\) 进行分析,我们以这种思路来看待高斯消元. 好现在还是从简单的开始. 首先,讲一下上一章中没讲完的内容--乘积的逆. 假设 \(A\) 和 \(B\) 均是可逆矩阵 ...

  7. SciTech-Mathmatics-Taylor Equation泰勒公式: 用幂级数(幂函数的和) 去无穷拟合 N阶可导函数(连续可导有N阶导数)

    https://www.mathsisfun.com/algebra/taylor-series.html https://math.libretexts.org/Bookshelves/Calcul ...

  8. Win11 64位系统为什么无法设置分屏的问题

    有些雨林木风官网的用户升级Win11 64位系统后,想要使用分屏功能的时候,却无法分屏,使用不上的问题,这是怎么回事呢?难道win11系统不能设置分屏吗?本文中,雨林木风小编就来分享具体的设置方法,有 ...

  9. .NETCore文件上传将文件保存到docker容器以外的文件夹

    最近在开发一个文件服务,用于公司内容各应用的文件保存和查询获取. 开发环境:windows10+.NET Core7.0+Mysql   发布环境 :Liunx+Docker 实现功能:文件服务提供接 ...

  10. 深度剖析:HR 人力资源软件排名背后的考量与推荐

    在数字化浪潮席卷企业管理的当下,HR 人力资源软件已成为企业提升管理效率.优化人才运营的关键工具.市场上软件琳琅满目,各类 "排名" 也层出不穷,但这些排名往往因评判标准不一而让人 ...