多线程
- 进程:正在活动(运行的)一个应用程序,就是一个进程
- 每一个进程 都至少有一个线程 (主线程)
- 线程:每一个线程都是独立的,可以执行任务
- 除了主线程以外的 都叫子线程
- 子线程可以有 很多个 但是线程是耗费资源的
- (一般最多不超过五条 注:三条最佳)
- 主线程操作什么样的任务?
(UI界面 按钮的点击 屏幕滚动 一切用户看的见的 都要在主线程当中操作) - 子线程也操作UI界面 会造成什么问题?
(听主线程 还是子线程) 程序退出后会清空线程的任务
主线程 下载20G电影 (耗时)
屏幕就会假死 卡住 下完才恢复- 比较大的耗时操作 又或者 用户看不到的操作 可以放到子线程当中去做 比如下载 解压缩 读取大型数据等可以在子线程当中进行操作
- 多线程的原理:
CPU在工作时,同一时间只能执行一个任务,
之所以可以造成多条线程一起执行任务的假象,是因为CPU在进行高速的线程切换(调度),来达到多个任务一起执行的效果 - 多线程的优点:
1.可以大大提高执行任务的效率
2.可以让用户 有更好的用户体验
缺点:
如果大量的开辟线程 会造成程序的卡顿 耗费过量的资源
物极必反
简介
iOS有三种多线程编程的技术,分别是:
(一)NSThread
(二)Cocoa NSOperation
(三)GCD(全称:Grand Central Dispatch)
这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。
三种方式的优缺点介绍:
1)NSThread:
优点:NSThread 比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
Cocoa NSOperation
优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
2) Cocoa operation 相关的类是 NSOperation ,NSOperationQueue。
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
3) GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都升级到7了,所以不用担心该技术不能使用。
介绍完这三种多线程编程方式,本文将依次介绍这三种技术的使用。
NSThread的使用
初始化线程
// 1.初始化 开辟一个子线程
// NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"123"];
// [thread start];
// 2.开辟一个子线程 不用开始自动执行
[NSThread detachNewThreadSelector:@selector(download:) toTarget:self withObject:@"123"];
selector :线程执行的方法,这个selector只能有一个参数,而且不能有返回值。
target :selector消息发送的对象
argument:传输给target的唯一参数,也可以是nil
第二种方式会直接创建线程并且开始运行线程,第一种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息
线程 休眠(延迟执行的效果)
[NSThread sleepForTimeInterval:5];
获得当前线程以及判断是否主线程
// 当前线程
NSLog(@"--------%@",[NSThread currentThread]);
// 判断当前线程 是否是主线程
NSLog(@"%d",[NSThread isMainThread]);
结束当前线程
[NSThread exit];
// 相当于 在这儿 return 只要退出当前的线程 后边的方法 一切方法都不执行了
Cocoa NSOperation的使用
使用NSOperation的方式有两种
一种是用定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
另一种是继承NSOperation
NSInvocationOperation的使用
// 需要把NSInvocationOperation对象 放进一个队列里 才能完成开启子线程
// 创建一个队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 给队列 设置 最大并发数
queue.maxConcurrentOperationCount = 1;
// 开启一个线程(相当于 一个任务)
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download:) object:@"12"];
// 把任务添加到 队列中
// 只需要把任务添加进队列中 不需要咱们来操作
// 会自动执行 不是添加一个任务就开启一个线程
// 这个操作是系统给的 有的线程 会被系统重复利用(优化)
[queue addOperation:operation];
GCD
- 全称Grand Central Dispatch
- GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。
- GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行。
dispatch queue分为下面三种:
Serial
又称为private dispatch queues,同时只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。
Concurrent
又称为global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的。
Main dispatch queue
它是全局可用的serial queue,它是在应用程序主线程上执行任务的。
常用的方法dispatch_async
GCD会自动根据任务在多核处理器上分配资源,优化程序。
系统给每一个应用程序提供了三个concurrent dispatch queues。这三个并发调度队列是全局的,它们只有优先级的不同。因为是全局的,我们不需要去创建。我们只需要通过使用函数dispath_get_global_queue去得到队列
// 获取全局队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});
});
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// 做请求的 耗时操作(同步请求)
// 在子线程中 同步请求 就相当于异步请求
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://image.dianping.com/Groups/GroupPic/2007-04-11/www123_1538656_88829_L.jpg"]];
UIImage *image = [UIImage imageWithData:data];
// 回到主线程刷新界面
// 取出主线程
// dispatch_get_main_queue();
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程要执行的任务 刷新UI
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
imageView.image = image;
[self.view addSubview:imageView];
[imageView release];
});
});
dispatch_group_async的使用
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group1");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"group2");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"group3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"updateUi");
});
dispatch_release(group);
dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:4];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async3");
});
dispatch_apply
dispatch_apply(5, globalQ, ^(size_t index) {
// 执行5次
});