多线程1
- 进程:
- 概念:系统中正在运行的一个应用程序
- 特点:每个进程都是独立的,运行在自己独有的且受保护的内存空间中。
- 线程:
- 概念:执行进程的单位,依赖于进程而存在
- NSThread创建线程的四种方法:
方法1
NSThread *threadC = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"创建线程1"];
方法2
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分离一条子线程"];
方法3
[self performSelectorInBackground:@selector(run:) withObject:@"创建后台线程"];
方法4(自定义)
[[[NSThread alloc]init]start];
- 进程和线程的比较
- 进程是cpu分配资源和调度的单位
- 线程是cpu进行调度的最小单元
- 一个正在运行的应用程序只能有一个进程,一个进程有不少于一个线程
- 同一个进程内的线程共享进程的资源
- 多线程:
- 原理:cpu分时快速在各个线程之间切换调用
- 优缺点:
- 优点:提高程序的执行效率以及内存和cpu的利用率
- 缺点:开线程会耗费资源,尤其是大量开线程的情况,并且会是程序设计更加复杂
- 主线程:
- 概念:程序运行自动开启的线程(主线程或UI线程)
- 作用:显示刷新UI界面、处理UI事件
- 注意点: 主线程不宜执行耗时较长的操作
- 线程安全问题小结:
- 售票员实例:
// 创建三个售票员线程,让三个售票员分时抢占资源 self.totalSize = 100; self.obj = [[NSObject alloc]init]; //创建线程 self.threadA = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; [self.threadA setName:@"售票员A"]; self.threadB = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; [self.threadB setName:@"售票员B"]; self.threadC = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; [self.threadC setName:@"售票员C"]; //开始执行 [self.threadA start]; [self.threadB start]; [self.threadC start]; // 让售票员分时抢占资源 -(void)saleTicket { while (1) { @synchronized(self) { NSInteger count = self.totalSize; if (count >0) { for (NSInteger i = 0; i<10000000; i++) { //演示耗时操作 } //检查余票的数量,如果发现有余票就卖出去一张 self.totalSize = count - 1; NSLog(@"%@卖出去了一张,还剩下%zd张票",[NSThread currentThread].name,self.totalSize); }else { NSLog(@"不要回家的,或者做飞机回去吧"); break; } } } }
- 售票员实例:
- 线程间通讯(下载图片实例):
- 注意图片设置一定要放到主线程中:
//1.确定url
NSURL *url = [NSURL URLWithString:@"http://img4.duitang.com/uploads/blog/201310/18/20131018213446_smUw4.thumb.700_0.jpeg"];
//2.下载图片的二进制数据到本地
NSData *data = [NSData dataWithContentsOfURL:url];
//3.转换格式 二进制数据转换UIimage
UIImage *image = [UIImage imageWithData:data];
NSLog(@"%@---",[NSThread currentThread]);
//4.回到主线程刷新UI
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//获得的时间是绝对时间
CFTimeInterval start = CFAbsoluteTimeGetCurrent();
- 线程的状态(创建、开启、运行、阻塞、死亡):
- 阻塞状态:
[NSThread sleepForTimeInterval:2.0]; [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
- 强行退出
[NSThread exit]
- GCD的使用(重点):
- 重要概念(队列是GCD中的概念):
- 并发队列(并发队列):允许多个任务同时执行
- 串行队列(主队列):多个任务依次执行,其中主队列必须在主线程中执行,当需要任务调度时首先检查主线程状态,若主线程阻塞,则停止调动
- 同步:必须得到该方法的返回值才能往下执行
- 异步:不必得到该方法的返回值依然可以向下执行
- 队列:(用来存放任务)
- 并发队列:(允许多个队列同时执行):
1)直接create dispatch_queue_create
2)全局并发队列 - 串行队列:(任务串行执行)
1)直接create dispatch_queue_create
2)主队列:
1)所有在主队列中的任务都会被放在主线程中执行
2)主队列中的任务在执行之前会先检查主线程的状态, 如果发现主线程当前正在执行任务那么会暂停队列中任务的调度- 队列与函数组合是否开线程以及开几条线程: - 异步函数+主队列:不会开线程,所有的任务串行执行
//1.获得主队列 dispatch_queue_t queue = dispatch_get_main_queue(); //2.封装任务 dispatch_async(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 同步函数+主队列:不会开线程,所有任务串行执行
//1.获得主队列 dispatch_queue_t queue = dispatch_get_main_queue(); //2.封装任务 dispatch_sync(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 异步函数+串行队列:开一条线程,串行执行
dispatch_queue_t queue = dispatch_queue_create("com.seemygo.www.download", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 异步函数+并发队列:开多条线程,并发执行
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 同步函数+并发队列:不会开线程,并发执行
dispatch_queue_t queue = dispatch_queue_create("com.seemygo.www.download", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 同步函数+串行:不会开线程,串行执行
dispatch_queue_t queue = dispatch_queue_create("com.seemygo.www.download", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- GCD线程间通信:
//1.开线程下载图片 //DISPATCH_QUEUE_PRIORITY_DEFAULT 0 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"%@----",[NSThread currentThread]); //1.1 确定url NSURL *url = [NSURL URLWithString:@"http://img4.duitang.com/uploads/blog/201308/24/20130824215734_ut8LZ.thumb.600_0.jpeg"]; //1.2 下载二进制数据到本地 NSData *data = [NSData dataWithContentsOfURL:url]; //1.3 转换图片 UIImage *image = [UIImage imageWithData:data]; //刷新UI dispatch_sync(dispatch_get_main_queue(), ^{ self.imageView.image = image; NSLog(@"%@--UI--",[NSThread currentThread]); }); });
- GCD中常用函数:
- 延时函数
1.方法 [self performSelector:@selector(task) withObject:nil afterDelay:2.0]; 2.方法 [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(task) userInfo:nil repeats:NO]; 3.方法 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{ NSLog(@"--GCD----%@",[NSThread currentThread]); });
- 一次性函数
// 整个应用程序只会执行一次,并且是线程安全的 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"--once---%@",[NSThread currentThread]); });
- 栅栏函数
1.在子线程中,确保前面任务执行完才会执行后面线程 dispatch_barrier_async(queue, ^{ NSLog(@"+++++++++++++%@",[NSThread currentThread]); });
- 快速迭代函数
//注意:主线程也会参与迭代的过程,里面的任务是并发执行的 !!!!不能传主队列 dispatch_apply(10, queue, ^(size_t index) { NSLog(@"%zd---%@",index,[NSThread currentThread]); });