IOS开发之多线程
IOS开发之多线程
1. 多线程简述
什么是多线程? 解决的问题?
多线程是指,编程中在主线程之外开辟的新线程,用于处理一些耗时的、并发的任务。使用多线程可以避免主线程的阻塞,
也对一个线程不容易实现的任务提供了思路。在多线程的知识中也涉及队列,锁等概念。
在这里科普一下队列的概念,队列:是管理线程的,相当于线程池,能管理线程什么时候执行。队列分为串行队列和并行队列。
串行队列:队列中的线程按顺序执行(不会同时执行)
并行队列:队列中的线程会并发执行,可能会有一个疑问,队列不是先进先出吗,如果后面的任务执行完了,怎么出去的了。
这里需要强调下,任务执行完毕了,不一定出队列。只有前面的任务执行完了,才会出队列。
(1)pthred多线程(POSIX标准),用在类unix系统上。
(2)NSThread 是对pthred面相对象封装
(3)NSOperation/NSOperationQueue 是使用GCD实现的一套Objective-C的API
(4)GCD(Grand Central Dispatch) 是基于C语言的底层API
使用较多,因为:1.支持多核心 2.c和block接口,易使用 3.功能强
2.NSThread
2.1线程的创建 线程的控制和通信
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
(1)创建线程 [self createThread]; (4) 线程的控制和通信 实例: A线程执行到10得时候让B线程结束 #pragma mark -- (1)创建线程 #pragma mark -- (4) 线程的控制和通信 -(void)createThread{ //线程 //顺序执行(串行执行) //[self taskA]; //[self taskB]; //并行执行(多个任务同时执行) //创建新的线程(第一种方式)---对应一个方法 //注意:创建之后不会立即执行,需要start NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(taskA) object:nil]; [thread1 start]; //创建新的线程(方式2) //创建后会立即执行 [NSThread detachNewThreadSelector:@selector(taskB:) toTarget:self withObject:thread1]; } -(void)taskA{ for (int i = 0; i < 20 ; i ++) { NSLog(@"A = %d",i); //每一次检测是否停止 if ([NSThread currentThread].isCancelled) { //退出当前线程 [NSThread exit]; } //线程每次休眠时间为0.25s [NSThread sleepForTimeInterval:0.25]; } } -(void)taskB:(NSThread *)thread{ for (int i = 0; i < 20 ; i ++) { NSLog(@"B = %d",i); if ( i == 10) { //停止线程,发送cancle消息 [thread cancel]; } [NSThread sleepForTimeInterval:0.25]; } }
2.2为什么使用多线程,解决什么样的问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma mark -- (2)为什么使用多线程,解决什么样的问题 -(void)whyUseThread{ //本质: 多线程主要解决执行耗时任务出现UI界面阻塞的问题 // 有时候为了同时执行多个任务(类似迅雷) //场景: 大麦网 -- 下载数据(耗时10s) // 图片(耗时5s) // 迅雷看看,加载1G视频文件 // 数据库中加载1w条数据 //演示 UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; button.frame = CGRectMake(0, 20, 320, 40); [button setTitle:@"耗时任务" forState:UIControlStateNormal]; [button addTarget:self action:@selector(startTask) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; } -(void)startTask{ //http://10.0.8.8/download/真机调试.tar.gz //http://10.0.8.8/download/真机调试.tar.gz NSString *urlString = @"http://10.0.8.8/download/真机调试.tar.gz"; NSData *data = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:urlString]]; NSLog(@"size = %u",data.length); }
2.3使用通知监听线程的结束
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
(3)使用通知监听线程的结束 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(dealThreadExit) name:NSThreadWillExitNotification object:nil]; #pragma mark -- (3)使用通知监听线程的结束 -(void)dealThreadExit{ NSLog(@"线程结束"); }
2.4线程的同步和锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
(5)线程的同步和锁 演示问题:多个线程访问同一块内存 为什么出问题 CPU和内存(_num) //项目开发:nonatomic当一个属性只有UI界面访问可以加上,提高速度 // 当这个属性页会被创建的子线程访问时不要加nonatomic保证线程的安全 // //如何解决问题 _lock = [[NSLock alloc]init]; [NSThread detachNewThreadSelector:@selector(add) toTarget:self withObject:nil]; [NSThread detachNewThreadSelector:@selector(sub) toTarget:self withObject:nil]; #pragma mark -- (5)线程的同步和锁 -(void)add{ for (int i = 0; i <100; i++) { [_lock lock]; _num ++; [_lock unlock]; NSLog(@"add = %d",_num); } } -(void)sub{ for (int i = 0; i <100; i++) { [_lock lock]; _num --; [_lock unlock]; NSLog(@"sub = %d",_num ); } }
2.5子线程如何更新UI
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
(6)子线程如何更新UI(非常重要,不会代码会出现问题) //限制: UI线程称为主线程 // 其他创建的线程称为工作子线程 // 注意: 不要再子线程中直接操作UI(间接让主线程操作UI) //下载进度更新 _progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(10, 200, 300, 20)]; [self.view addSubview:_progressView]; [NSThread detachNewThreadSelector:@selector(downloadNetworkdata) toTarget:self withObject:nil]; #pragma mark -- (6)子线程如何更新UI(非常重要,不会代码会出现问题) -(void)downloadNetworkdata{ for (int i = 0; i< 100; i++) { //_progressView.progress +=.01; [self performSelectorOnMainThread:@selector(updataUI) withObject:nil waitUntilDone:YES]; [NSThread sleepForTimeInterval:.1]; } } -(void)updataUI{ _progressView.progress +=.1; if (_progressView.progress == 1) { NSLog(@"========="); [_progressView removeFromSuperview]; } }
3.NSOperation
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //NSOperation 的使用 (操作、行动) //(1)什么是NSOperation,作用 // NSOperation和NSThread相似,实现多线程的一种机制 // 在NSThread做了更高的抽象,加入了block,比NSThread更简单易用 //NSOperation抽象类,使用NSInvocationOperation和NSBlockOperation //(2)NSInvocationOperation NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(task1) object:nil]; //注意: 创建后需要执行,执行的时候默认是同步的 // [invocationOperation start]; //(3)NSBlockOperation NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i <20 ; i++) { NSLog(@"B = %d",i); } }]; // [blockOperation start]; //(4)NSOperationQueue操作队列,理解为:任务列表 //注意:任务会异步平行执行 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; [queue addOperations:@[invocationOperation,blockOperation] waitUntilFinished:NO]; [queue addOperation:invocationOperation]; [queue addOperation:blockOperation]; } -(void)task1{ for (int i = 0; i <20 ; i++) { NSLog(@"A = %d",i); } }
4.GCD
4.1GCD的使用 (Grand Central Dispatch 简写)
好处: 1.支持多核心
2.C和block接口, 易于使用
3.较晚出现, 功能强大, 推荐使用
4.2创建一个异步任务
-(void)createAsyncTask { // 创建一个异步任务 //参数1: 传入queue, 有三种queue // main queue 主队列(UI主线程) // global queue 全局队列(理解为工作线程) // 自定义的queue dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ for (int i=0; i<20; i++) { NSLog(@"A = %d",i); } }); dispatch_async(queue, ^{ for (int i=0; i<20; i++) { NSLog(@"B = %d",i); } }); }
4.3模拟网络下载
-(void)simulateNetworkDownload { progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(10, 100, 300, 20)]; [self.view addSubview:progressView]; //GCD最简单开启异步任务的形式 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for(int i=0; i<100; i++) { //子线程中不能直接更新UI //progressView.progress += 0.01; dispatch_async(dispatch_get_main_queue(), ^{ progressView.progress += 0.01; }); NSLog(@"progress = %.2f%%",progressView.progress*100); [NSThread sleepForTimeInterval:0.1]; } //最后显示对话框 dispatch_async(dispatch_get_main_queue(), ^{ UIAlertView *alertView = [[UIAlertView alloc] init]; alertView.message = @"下载完成"; [alertView addButtonWithTitle:@"取消"]; [alertView show]; }); }); }
4.4只执行一次, 实现单例(推荐实现的方式,线程安全)
-(void)runOnce { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"只执行一次的代码"); }); }
4.5延时执行
-(void)delayRun { //5s 后输出hehe dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"he he"); }); }
4.6同时执行多个任务, 等待所有任务执行完成进行处理(类似迅雷
实例: 迅雷所有任务完成之后自动关机
-(void)groupRun { // group 任务组 dispatch_group_t group = dispatch_group_create(); //添加任务 7s完成 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (int i=0; i<100; i++) { NSLog(@"A = %d",i); [NSThread sleepForTimeInterval:0.07]; } }); // 5s完成 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (int i=0; i<100; i++) { NSLog(@"B = %d",i); [NSThread sleepForTimeInterval:0.05]; } }); // 10s完成 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (int i=0; i<100; i++) { NSLog(@"C = %d",i); [NSThread sleepForTimeInterval:0.1]; } }); dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"所有任务执行完成, 自动关机"); }); }