多线程编程-NSThread
每个iOS应用程序都有个专门用来更新显示UI界面、处理用户的触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验。一般的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程编程是防止主线程堵塞,增加运行效率的最佳方法
iOS支持多个层次的多线程编程,层次越高的抽象程度越高,使用也越方便,也是苹果最推荐使用的方法。下面根据抽象层次从低到高依次列出iOS所支持的多线程编程方法:
- Thread :是三种方法里面相对轻量级的,但需要管理线程的生命周期、同步、加锁问题,这会导致一定的性能开销
- Cocoa Operations:是基于OC实现的,NSOperation以面向对象的方式封装了需要执行的操作,不必关心线程管理、同步等问题。NSOperation是一个抽象基类,iOS提供了两种默认实现:NSInvocationOperation和NSBlockOperation,当然也可以自定义NSOperation
- Grand Central Dispatch(简称GCD,iOS4才开始支持):提供了一些新特性、运行库来支持多核并行编程,它的关注点更高:如何在多个cpu上提升效率
多线程的原理
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
这篇文章简单介绍了第一种多线程编程的方式,主要是利用NSThread这个类,一个NSThread实例代表着一条线程
一、NSthread的初始化
- 动态方法
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument
NSThread * thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doAction) object:nil];
//线程名
thread1.name = @"thread1";
//线程优先级,0 ~ 1
thread1.threadPriority = 1.0;
//开启线程
[thread1 start];
参数解析:
selector :线程执行的方法,这个selector最多只能接收一个参数
target :selector消息发送的对象
argument : 传给selector的唯一参数,也可以是nil
- 静态方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//通过类方法创建线程,不用显示的开启start
[NSThread detachNewThreadSelector:@selector(doAction) toTarget:self withObject:nil];
- 隐式创建线程的方法:
//隐式创建多线程
[self performSelectorInBackground:@selector(doAction:) withObject:@"CYY"];
获取线程
//获取当前线程
NSThread *current = [NSThread currentThread];
//获取主线程
NSString *main = [NSThread mainThread];
可以在子线程获取主线程
NSLog(@"mainThread - %@",[NSThread mainThread]);
暂停当前线程
//阻塞状态
[NSThread sleepForTimeInterval:2];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
强制停止线程
[NSThread exit];
注意:一旦线程停止(死亡)了,就不能再次开启任务
线程间通信
主线程执行操作
[self performSelectorOnMainThread:@selector(changeMainThread:) withObject:image waitUntilDone:NO];
在指定线程上执行操作
[self performSelector:@selector(changeMainThread:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
在当前线程执行操作
[self performSelector:@selector(setImage:) withObject:nil]
waitUntilDone:是什么意思?
YES:等待loadImage:这个方法执行完毕之后,再执行当前线程后续的操作
NO:不等待loadImage:这个方法执行完毕,就执行当前线程后续的操作
实例:卖票问题
有两个线程,两个线程之间不能同时进行,需要用到互斥锁
- (void)viewDidLoad {
[super viewDidLoad];
self.tickets = 20;
NSThread * thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
// thread1.name = @"computer";
[thread1 start];
NSThread * thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
// thread2.name = @"phone";
[thread2 start];
}
- (void)saleTicket {
while (1) {
//互斥锁
@synchronized(self) {
[NSThread sleepForTimeInterval:2];
if (self.tickets > 0) {
NSLog(@"%@ 还有余票 %@ 张",[NSThread currentThread],@(self.tickets));
self.tickets -- ;
} else {
NSLog(@"票卖完了");
break;
}
}
}
}
NSThread优缺点
1、优点:NSThread比其他两种多线程方案较轻量级,更直观地控制线程对象
2、缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
多线程的优缺点
一、多线程的优点
1、能适当提高程序的执行效率
2、能适当提高资源利用率(CPU、内存利用率)
二、多线程的缺点
1、开启线程需要占用一定的内存空间(主线程:1M,其他线程512kb),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
2、线程越多,CPU在调度线程上的开销就越大
3、程序设计更加复杂:比如线程之间的通信、多线程的数据共享