88-NSThread的使用

来源:互联网 发布:中日韩 餐具知乎 编辑:程序博客网 时间:2024/05/18 03:01
NSThread的使用 

NSThread:一个NSThread就相当于一个线程
<一>创建并启动线程 -(instancetype)initWithTarget:selector:object:
参数
self:提供 selector 方法的对象,通常是self
selector:要执行的方法
object:给方法提供的参数
NSThread *thread=[[NSThread allocinitWithTarget:self selector:@selector(thredDemo) object:nil];
[thread 
start];

1.设置线程属性

注意:只有alloc init的方式创建线程才可以设置线程属性

1>设置线程的名字 name  
thread.name=
@"Thread A";
作用:在多线程开发时可以用来判断到底是谁在执行任务,方便排错

2>获取当前主线程 mainThread
NSThread *threadMain=[NSThread mainThread];

3>设置线程优先级 threadPriority
thread1.threadPriority=
1.0;
取值:浮点数0.0-1.0,默认是0.5,取值越大,优先级别越高,优先级最高的是1.0
作用:优先级越高代表CPU调度线程频率越高,而不是优先级高的任务会先执行完
建议:不要修改线程的优先级,因为做多线程开发的主要目的,将耗时操作放在后台
注意:手动修改优先级会造成安全隐患:优先级反转,会非常麻烦

4>设置线程的堆栈 stackSize
thread1.
stackSize=1024 * 1024//1M
注意:取值只能是4的倍数
默认主线程和子线程堆栈(占用内存空间)都是512K(注意:以前主
线程是512K)
线程执行前,堆栈大小512K,线程完成后,堆栈大小0K,内存空间
注意:线程执行完毕后,由于内存空间已经被释放,不能再次启动

5>线程的状态属性
isExecuting 只读属性,线程是否正在执行

isCancelled 只读属性,线程是否被取消
isFinished 只读属性,线程是否完成

2.执行线程的方法
1>start 将线程对象加入到可调度线程池等待CPU调度
2>cancel 取消线程,在线程执行方法中需要增加 isCancelled 判断,YES是就直接返回

3.NSThread线程的类方法
1>[NSThread currentThread] 
当前线程
object:给方法提供的参数
作用:可以判断当前程序执行所在的线程
number == 1,表示主线程
number != 1,表示子线程(不是1都是子线程,没有具体是几)

2>+isMultiThreaded 是否多线程
3>+mainThread 返回主线程对象

4>+isMainThread 是否主线程

<二>隐式创建线程并启动 -performSelectorInBackground:withObject:
注意:是NSObeject的分类方法
@selector 要执行的方法
简单快捷但无法详细设置线程属性信息
[
self performSelectorInBackground:@selector(thredDemo) withObject:nil];

<三>创建线程后自动启动线程 +detachNewThreadSelector:toTarget:withObject:
self:提供 selector 方法的对象,通常是self
selector:要执行的方法
简单快捷但无法详细设置线程属性信息
[
NSThread detachNewThreadSelector:@selector(thredDemo) toTarget:self withObject:nil];

<四>线程间通讯(都是NSObeject的分类方法)
1.什么是线程间通讯:将数据从一个线程传递给另一个线程,最常见的应用场景,就是从后台(子线程)下载完数据,完成之后在主线程更新UI
注意:不是所有在子线程更新UI都会出问题,但是一定要在主线程更新UI,否则会出现问题

2.UI线程-主线程
所有UI更新都应该在主线程上进行
原因:几乎所有的UIKit类都不是线程安全的
取舍:苹果公司共同约定,所有程序员更新UI统一在主线程上进行,用不加锁换取高效率和低耗电率

3.实现线程间通讯

1>在主线程执行selector方法 performSelectorOnMainThread
参数
selector 要执行的方法
withObject 传递给 selector 方法的参数
waitUntilDone是否等待主线程执行的方法结束 
YES:是 NO:不是,一般都是NO,不会等待主线程的方法执行结束
[
self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];

线程间通讯注意:
(子线程)下载完数据,完成之后在主线程更新UI
注意:不是所有在子线程更新UI都会出问题,但是一定要在主线程更新UI,否则会出现问题

2>在
指定线程执行selector方法 -performSelector:onThread:withObject:waitUntilDone:
参数
onThread:指定哪一个线程执行方法
selector 要执行的方法
withObject 传递给 selector 方法的参数
waitUntilDone是否等待主线程执行的方法结束 
YES:是 NO:不是,一般都是NO,不会等待主线程的方法执行结束
[
self performSelector:@selector(setImage:) onThread:[NSThread mainThreadwithObject:image waitUntilDone:NO];

<五>线程的状态
正常的线程状态: 创建线程--> 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
    
1.创建 start
实例化对象,向线程发送 start 消息,线程对象被加入到可调度线程池中等待CPU调度
NSThread *thread=[[NSThread alloc] initWithTarget:
self selector:@selector(demo) object:nil];
[thread start];

2.就绪 Runnable
detach 方法和 performSelectorInBackground 方法会直接实例化一个线程对象并加入“可调度线程池”
注意:CPU 负责调度“可调度线程池”中线程的执行

3.运行 Running
线程执行完成之前,状态可能会在就绪和运行之间来回切换
注意:就绪和运行之间的状态变化由 CPU 负责,程序员不能干预

4.阻塞 Blocked
当满足某个预定条件时,可以使用休眠或锁阻塞线程执行
1>睡眠
one:阻塞当满足某个预定条件时,使线程休眠 sleepForTimeInterval
让当前线程睡眠几秒
[NSThread sleepForTimeInterval:
5];

two:满足某一条件,继续睡眠休眠 sleepUntilDate
从现在开始睡
2秒 dateWithTimeIntervalSinceNow
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:
2.0]];

2>锁@synchronized(self)
注意:线程对象进入阻塞状态后,会被从可调度线程池中移出,CPU 不再调度

5.死亡 Dead
1>正常死亡,线程执行完毕
2>非正常死亡
one:线程内死亡 [NSThread exit] 强制退出线程
[NSThread exit]  死亡注意:
a:一旦线程被强行终止,后续所有代码都不会在执行,线程对象也会被销毁
b:使用exit方法强制退出线程,没有给线程机会释放执行过程中申请的资源,会直接退出线程,调用之前,要考虑释放对象
c:ARC 中通常不用考虑,如果涉及到 C 语言的混编,如果分配了 C 语言的对象,在 exit 之前,一定要release释放对象

two:线程外死亡[threadObj cancel]通知线程对象取消
在线程执行方法中需要增加 isCancelled 判断如果 isCancelled == 
YES,直接返回

注意:
1>死亡后 stackSize == 0,内存空间被释放
2>如果是发送 calcel 消息,线程对象的 isCancelled 属性为YES
3>死亡后线程对象的 isFinished 属性为 YES

<六>多线程的安全隐患—>资源共享(资源争夺)
1.什么是资源共享
1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
当多个线程访问同一块资源时,容易引发数据错乱和数据安全的问题

2.解决安全隐患—>互斥锁(同步锁) @synchronized
1>概念:互斥锁能够保证同一时间内,只有一条线程执行
2>使用互斥锁优缺点:
优点
能够有效防止因多线程抢夺资源而造成的数据安全问题
缺点
one:使用互斥锁代价昂贵,会大量消耗CPU性能
two:锁定范围只允许一条线程进行,无法并发,效率下降(加锁就是为了避免并发)
three:互斥锁定代码的范围,应该尽量的小
three:之所以没有智能提示,就是因为互斥所的性能不好,苹果不建议使用
3>互斥锁参数 
@synchronized (self)
{
self —> 是一个能够加锁的NSObject对象,而且一定要保证所有线程对象都能够访问到(局部变量不可以)
一般情况而言:self(当前视图控制器) 是最方便使用的加锁对象,如果程序中只有一个地方涉及到,绝大多数可以考虑使用。
}
3.相关专业术语—>线程同步
线程同步:多条线程按顺序的执行任务,互斥锁,就是使用了线程同步技术

4.互斥锁和自旋锁的异同
1>共同点:
都可以保证同一时间只有一条线程执行锁定范围的代码
2>不同点:
互斥锁:当发现要执行的代码被其他线程锁定后,线程会进入休眠状态,等待解锁之后,线程会被再次唤醒,性能不高。
自旋锁:当发现要执行的代码被其他线程锁定后,会以死循环的方式,监听是否解锁,一旦解锁,立刻执行,执行性能会高,适合锁定非常短的代码

<七>原子属性和非原子属性
1.原子属性: natomic
原子属性是能够保证线程安全的属性,原子属性内部也有一把锁,为setter方法加锁
补充:原子属性在写入文件时的作用:在写入数据时,先将数据写入到一个临时文件,等所有完全写入后,在复制到目标文件,防止在写入过程中,被其他文件所覆盖

2.非原子属性: nonatomic,不会为setter方法加锁

3.nonatomic和atomic对比
atomic:线程安全,需要消耗大量的资源
nonatomic:非线程安全,适合内存小的移动设备

4.iOS开发的建议
所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力



<八>线程安全
1>什么是线程安全:多个线程同时对一个资源进行读写操作的时候,同样能保证结果是正确的,实现线程安全,必须要使用锁。
注意:只要使用锁,就会降低性能
0 0