ios 多线程之NSThread篇举例详解
这篇博客是接着总篇iOS GCD NSOperation NSThread等多线程各种举例详解写的一个支篇。总篇也包含了此文的链接。本文讲解的知识点有NSThread的开始、取消、在当前线程执行任务、线程通信、线程同步、延时函数等。附上:demo下载地址。
一、NSThread介绍
优点:NSThread 比其他两个轻量级。
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。
二、开始和取消
举个例子,注释写的很详细,不多说啦。
//开始 - (IBAction)start:(id)sender { //实例方法开始 _myThread = [[NSThread alloc]initWithTarget:self selector:@selector(startCount) object:nil]; [_myThread start]; //静态方法开始 // [NSThread detachNewThreadSelector:@selector(startCount) toTarget:self withObject:nil]; //隐式开始 // [self performSelectorInBackground:@selector(startCount) withObject:nil]; } //计时 -(void)startCount { for (NSInteger i = 0; i< 1000; i++) { //根据线程是否取消的标志位退出该任务 if (_myThread.cancelled) { return; } [NSThread sleepForTimeInterval:1.0f]; NSLog(@"%ld",i); } } //取消线程 - (IBAction)cancel:(id)sender { //并没有真正取消该线程,只是给该线程设置了一个标志位 [_myThread cancel]; }
注意:[_myThread cancel];并没有取消该线程,只是给该线程设置了一个标志位,需要到具体任务里根据线程的.cancelled属性判断来取消。
三、线程通信
开一个子线程执行某任务,完成后回到主线程更新UI,实现线程通信,举个例子。
//线程通信 - (IBAction)communication:(id)sender { //开一个子线程执行某任务,完成后回到主线程更新UI,实现线程通信 [NSThread detachNewThreadSelector:@selector(communicationTask) toTarget:self withObject:nil]; } //线程通信任务 - (void)communicationTask { NSLog(@"当前线程%@",[NSThread currentThread]); //模拟一个3秒的任务,完成后到主线程更新UI [NSThread sleepForTimeInterval:3]; [self performSelectorOnMainThread:@selector(updateUI:) withObject:[UIColor redColor] waitUntilDone:YES]; } //更新UI - (void)updateUI:(UIColor *)color { self.view.backgroundColor = color; NSLog(@"我变红了%@",[NSThread currentThread]); }
打印结果:
分析结论:从communicationTask所在线程回到主线程更新了UI,实现了线程间通信。
四、线程同步
举一个经典的抢票的例子。抢票涉及到多个线程对同一个数据进行操作,NSThread是要自己管理线程同步的,让同一个时间只有一个线程操作该数据,那么我们就要用到加锁。
@interface NSThreadViewController () { NSInteger tickets;//剩余票数 NSInteger sellNum;//卖出票数 NSThread* thread1;//买票线程1 NSThread* thread2;//买票线程2 NSLock *theLock;//锁 } @property (nonatomic, strong) NSThread* myThread; @end
//(线程同步)2人抢票 - (IBAction)twoPeopleBuy:(id)sender { tickets = 9; sellNum = 0; theLock = [[NSLock alloc] init]; thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(buy) object:nil]; [thread1 setName:@"Thread-1"]; [thread1 start]; thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(buy) object:nil]; [thread2 setName:@"Thread-2"]; [thread2 start]; } //买票 -(void)buy{ while (TRUE) { //上锁 [theLock lock]; if(tickets >= 0){ [NSThread sleepForTimeInterval:0.09]; sellNum = 9 - tickets; NSLog(@"当前票数是:%ld,售出:%ld,线程名:%@",tickets,sellNum,[[NSThread currentThread] name]); tickets--; }else{ break; } [theLock unlock]; } }
打印结果:
分析结论:加锁来实现线程同步后,票是一张一张卖出去的。当把锁去掉就会出现如下局面:
两个线程同时操作一个数据,最后出现了负数。
五、NSThread延时
此方式在主线程和子线程中均可执行。是一种阻塞的执行方式,建方放到子线程中,以免卡住界面,没有找到取消执行的方法。
[NSThread sleepForTimeInterval:3];//延时当前线程