iOS 多线程之 NSThread的基本使用
Posted 幻影-2000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 多线程之 NSThread的基本使用相关的知识,希望对你有一定的参考价值。
一个NSThread对象就代表一条线程 下面是NSThread开启线程的方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self openThreadWithNSThread]; [NSThread mainThread];//获取主线程 [NSThread currentThread]; //获取当前线程 } - (void) openThreadWithNSThread { /* *第一个参数 目标对象 self *第二个参数 方法选择器 调用的方法 *第三个参数 前面调用方法需要传递的参数 可以为nil */ //第一种方式 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:@"ABC"]; thread.name = @"线程1"; //0.0-1.0之间 1.0最高 0.0最低 默认0.5 越高线程被调用的频率越大 thread.threadPriority = 1.0; [thread start];//需要手动调用启动线程 //第二种方式 [NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:@"abc2"];//自动启动线程 //第三种方式 [self performSelectorInBackground:@selector(threadAction:) withObject:@"开启一条后台线程"];//开启一条后台线程 //block方式 NSThread *thread1 = [[NSThread alloc] initWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; thread1.name = @"线程2"; thread1.threadPriority = 0.5; [thread start]; //或者 [NSThread detachNewThreadWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; } - (void) threadAction:(NSString *)params { NSLog(@"%@ and %@",params,[NSThread currentThread].name); }
NSThread 创建线程的生命周期
当线程中的任务执行完毕后 线程被释放掉 可以继承NSThread创建一个新类 重写dealloc方法来验证
线程的状态
当线程处于就绪状态时线程会被移到可调度线程池里面(CPU只调度此线程池里面的线程),当处于阻塞状态时,线程会被移出可调度线程池,当处于死亡状态时 先移出线程池,再从内存中释放。
- (void)threadStatus { //创建线程 在内存中创建一个线程 (新建状态) NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction1) object:nil]; [thread start]; //就绪状态 会被放倒可调度线程池里面 只有在可调度线程池里面的线程才是可以被CPU调度的 } //一旦线程死亡了 就不能再次开启任务 - (void)threadAction1 { //运行状态 for (NSInteger i = 0; i < 10000; i ++) { if (i == 5000) { //[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; [NSThread sleepForTimeInterval:2.0];//阻塞状态 移出线程池 //两秒钟过后 再次进入就绪状态 等待调度进入运行状态 } if (i == 8000) { //break;//任务完成 自然死亡 [NSThread exit]; //废除线程强制进入死亡状态 } NSLog(@"%@",[NSThread currentThread]); } //执行完方法之后死亡状态 }
线程的安全 也非常重要 这里介绍一种方法 后面会着重介绍
多线程的安全隐患
资源共享
1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象 同一个变量 同一个文件 此时就很容易引发数据错乱和数据安全问题.
安全隐患原因分析
安全隐患的解决
问题代码
#import "ViewController.h" @interface ViewController () @property (nonatomic,strong) NSThread *threadA;//售票员A @property (nonatomic,strong) NSThread *threadB;//售票员B @property (nonatomic,strong) NSThread *threadC;//售票员C @property (nonatomic,assign) NSInteger totalCount; @end @implementation ViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //设置总票数 self.totalCount = 1000; self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadA.name = @"售票员A"; [self.threadA start]; self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadB.name = @"售票员B"; [self.threadB start]; self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadC.name = @"售票员C"; [self.threadC start]; } //售票 - (void)saleTicket { while (1) { NSInteger count = self.totalCount; if (count > 0) { for (NSInteger i = 0; i < 100000; i ++) { } //卖出去一张票 self.totalCount = count - 1; NSLog(@"%@ 卖出去一张票 还剩%zd张票",[NSThread currentThread].name,self.totalCount); }else { NSLog(@"票卖完了"); break; } } }
打印结果
2018-03-05 22:28:38.600491+0800 NSThreadDemo[1016:86284] 售票员A 卖出去一张票 还剩999张票 2018-03-05 22:28:38.600493+0800 NSThreadDemo[1016:86285] 售票员B 卖出去一张票 还剩999张票 2018-03-05 22:28:38.600519+0800 NSThreadDemo[1016:86286] 售票员C 卖出去一张票 还剩999张票
问题解决代码
#import "ViewController.h" @interface ViewController () @property (nonatomic,strong) NSThread *threadA;//售票员A @property (nonatomic,strong) NSThread *threadB;//售票员B @property (nonatomic,strong) NSThread *threadC;//售票员C @property (nonatomic,assign) NSInteger totalCount; @end @implementation ViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //设置总票数 self.totalCount = 1000; self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadA.name = @"售票员A"; [self.threadA start]; self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadB.name = @"售票员B"; [self.threadB start]; self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.threadC.name = @"售票员C"; [self.threadC start]; } //售票 - (void)saleTicket { while (1) { //锁必须是全局唯一的 @synchronized(self) { NSInteger count = self.totalCount; if (count > 0) { for (NSInteger i = 0; i < 100000; i ++) { } //卖出去一张票 self.totalCount = count - 1; NSLog(@"%@ 卖出去一张票 还剩%zd张票",[NSThread currentThread].name,self.totalCount); }else { NSLog(@"票卖完了"); break; } } } }
互斥锁的优缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源
互斥锁的使用前提:多条线程抢夺同一块资源
相关专业术语:线程同步,多条线程按顺序地执行任务
互斥锁,就是使用了线程同步技术
原子和非原子属性
OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁
1 @property (assign, atomic) int age; 2 3 - (void)setAge:(int)age 4 { 5 6 @synchronized(self) { 7 _age = age; 8 } 9}
原子和非原子属性的选择
nonatomic和atomic对比
atomic:线程安全,需要消耗大量的资源(并非是真正的线程安全 更准确的说应该是读写安全,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。)
nonatomic:非线程安全,适合内存小的移动设备
ios开发的建议
所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
以上是关于iOS 多线程之 NSThread的基本使用的主要内容,如果未能解决你的问题,请参考以下文章