当你想在线程中执行一个很长的任务,但又不希望它阻塞应用程序其余部分的执行时,子线程尤其有用,你可以将任务放在子线程中来执行,避免阻塞应用程序的主线程,让主线程处理用户界面和与事件相关的操作,子线程用于将大型任务划分为几个较小的任务,这能会使多核计算机上的性能提高。
NSThread使用
- 获取主线程&当前线程
// 获取主线程 NSThread *mainThred = [NSThread mainThread]; // 获取当前线程 NSThread *currentThred = [NSThread currentThread];
- 线程的创建
- 这种创建方式需要手动启动线程
- (void)viewDidLoad {
[super viewDidLoad];
// 创建一条线程(方式一)
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:@"我是一条线程"];
// 启动线程
[thread start];
}
- 以下几种方式创建线程会自动启动线程,但是出了方式二以外的几种创建方式都无法对线程进行详细的设置。
// 方式二
NSThread *thread = [[NSThread alloc] initWithBlock:^{
}];
// 方式三
[NSThread detachNewThreadWithBlock:^{
NSLog(@"thread=%@-------",[NSThread currentThread]);
}];
// 方式四
[NSThread detachNewThreadSelector:@selector(runThread:) toTarget:self withObject:@"线程"];
// 方式五
[self performSelectorInBackground:@selector(runThread:) withObject:@"线程"];
- 常用属性
// 优先级 0.0 ~ 1.0 默认 0.5 数值越大优先级越高
@property double threadPriority ;
/** NSQualityOfService:
NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操作,比如处理点击事件,绘制图像到屏幕上
NSQualityOfServiceUserInitiated:次高优先级,主要用于执行需要立即返回的任务
NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级
NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务
NSQualityOfServiceBackground:后台优先级,用于完全不紧急的任务
*/
@property NSQualityOfService qualityOfService;
// 线程名称(区别多条线程)
@property (nullable, copy) NSString *name;
// 线程正在执行
@property (readonly, getter=isExecuting) BOOL executing;
// 线程执行结束
@property (readonly, getter=isFinished) BOOL finished;
// 线程是否可以取消
@property (readonly, getter=isCancelled) BOOL cancelled;
- 常用方法
-(void)start; // 启动线程
-(BOOL)isMainThread; // 是否为主线程
-(void)setName:(NSString *)n; // 设置线程名称
-(void)cancel ;// 取消线程
-(void)isExecuting; // 判断线程是否正在执行
-(void)isCancelled; // 判断线程是否撤销
-(void)isFinished; // 判断线程是否已经结束
+(void)currentThread; // 获取当前线程
+(BOOL)isMultiThreaded; // 当前代码运行所在线程是否是子线程
+(void)sleepUntilDate:(NSDate *)date; //当前代码所在线程睡到指定时间
+(void)sleepForTimeInterval:(NSTimeInterval)ti; //当前线程睡多长
时间
+(void)exit; // 退出当前线程
- 线程的生命周期
当线程中的任务执行完成过后被释放
- 线程的状态
- 就绪状态:当前线程准备就绪,CPU调度当前线程--->变为运行状态。
- 运行状态:当前线程运行中,当CPU调度其他线程--->变为就绪状态。
- 阻塞状态:当前线程调用sleep方法,等睡眠时间结束--->变为就绪状态。
- 死亡状态:线程任务执行完成,移出可调度线程池,被释放掉。
线程的安全
-
如果多个线程(同时)访问同一资源时,很容易引发数据错乱和数据安全问题。
线程安全.png
总共有100张电影票,当售票员01和02同时获取到余票为一百张时,01卖出了80张余票为20张,02卖出了21张余票为79张,实际上他们卖出了101张但是总票数只有100张,这样就多出一个人没法看电影,这就是引发数据错乱。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,strong) NSThread *threadA;
@property (nonatomic,strong) NSThread *threadB;
@property (nonatomic,strong) NSThread *threadC;
@property (nonatomic,assign) NSInteger totalCount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 总共设置100 票
self.totalCount = 100;
self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
self.threadA.name = @"售票员A";
self.threadB.name = @"售票员B";
self.threadC.name = @"售票员C";
[self.threadA start];
[self.threadB start];
[self.threadC start];
}
-(void)runThread:(id)obj{
while (1) {
NSInteger count = self.totalCount;
if (count>0) {
for (int i = 0; i<1000; i++) { // 模拟售票时间
self.totalCount = count - 1;
}
NSLog(@"%@卖了一张票还剩%zd",[NSThread currentThread].name,self.totalCount);
}else{
NSLog(@"售完了");
break;
}
}
}
@end
![](https://img.haomeiwen.com/i4027600/82f05e0016dfc69a.png)
- 互斥锁 「@synchronized () { }」
- 锁是必须全局唯一的
- 注意加锁的位置。
- 注意加锁条件(多线程访问同一资源)
- 加锁的结果会导致线程同步(多条线程在同一条线上执行,按顺序执行任务)
-(void)runThread:(id)obj{
while (1) {
// 锁:必须是全局唯一的
@synchronized (self) { // 互斥锁
NSInteger count = self.totalCount;
if (count>0) {
for (int i = 0; i<1000; i++) { // 模拟售票时间
self.totalCount = count - 1;
}
NSLog(@"%@卖了一张票还剩%zd",[NSThread currentThread].name,self.totalCount);
}else{
NSLog(@"售完了");
break;
}
}
}
}
![](https://img.haomeiwen.com/i4027600/d01ca15f7ac7199e.png)
非原子属&性原子
- nonatomic:非原子属性,不会为setter方法加锁,非线程安全,适合内存小的移动设备。
- atomic:原子属性,为setter方法加锁(默认就是atomic),线程安全,需要耗费大量的资源。
线程间通信
- 一个线程传递数据给另一个线程
- 在一个线程中执行完成任务,转到另一个线程中继续执行任务(列如子线程下载图片,主线程显示图片)
- 常用方法
// 从当前线程切换到主线程中
-(void)performSelectorOnMainThread:(SEL)aSelector
withObject:(id)arg
waitUntilDone:(BOOL)wait;
// 从当前线程切换到指定的线程
-(void)performSelector:(SEL)aSelector
onThread:(NSThread *)thr
withObject:(id)arg
waitUntilDone:(BOOL)wait;
网友评论