一、什么是线程:
1、1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)
2、线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行
3、1个线程中任务的执行是串行的,如果要在1各线程中执行多个任务,那么只能一个一个地按顺序执行这些任务,也就是说,在同一时间内,1个线程只能执行1个任务
二、什么是多线程
1、1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务
例:进程 ——>车间 ,线程——>车间工人
2、多线程的原理:同时间,CPU只能处理1条线程,只有1条线程在工作(执行),多线程并发(同时)执行,其实质是CPU快速地在多条线程之间调度(切换),当CPU调度线程的速度足够块,就造成了多线程并发执行的假象
三、什么是主线程
1、一个iOS程序运行后,默认会开启1条线程,成为“主线程”或“UI线程”
2、主线程的主要作用:显示,刷新UI界面 ;处理UI事件(比如点击事件、滚动事件、拖拽事件等)
3、使用主线程需要注意:别将比较耗时的操作放到主线程,会卡UI(阻塞UI)
四、iOS中多线程的实现方案
1、pthread
简介:一套通用的线程API,适用于UnixLinuxWindows等系统;跨平台,可移植;使用难度大。
语言:C
线程生命周期:程序员管理
使用频率:几乎不用
示例代码:
2、NSThread
简介:使用更加面向对象,简单易用,可直接操作线程对象
语言:OC
线程生命周期:程序员管理
使用频率:偶尔使用
创建线程实例代码:
创建方式一:
创建方式二:
创建方式三(隐式创建并启动):
线程安全:
1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,很容易引发数据错乱和数据安全问题。
线程安全代码实例(加互斥锁):
互斥锁使用格式: @synchronized(self){ //需要锁定的代码}
注意:锁定1份代码只能用1把锁,用多把锁是无效的,且加过多的锁会消耗大量的CPU资源降低程序效率
线程间通讯:
线程其他方法:
3、GCD
简介:旨在替代NSThread等线程技术,充分利用设备的多核
简介:C
线程生命周期:自动管理
使用频率:经常使用
3.1 什么是GCD:全称(Grand Central Dispatch,可译为"伟大的调度中心")
3.2 GCD的优势:
3.2.1 GCD是苹果公司为多核的并行运算提出的解决方案;
3.2.2 GCD会自动利用更多的CPU内核(比如双核,四核)
3.2.3 GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
3.3 GCD中有2个核心概念
3.3.1 任务:执行什么操作
3.3.2 队列:用来存放任务
3.4 GCD的使用就2个步骤:
步骤一:定制任务(确定想做的事情)
步骤二:将任务添加到队列中(CGD会自动将队列中的任务取出,放到对应的线程中执行),任务的取出遵循队列的FIFO原则,先进先出,后进后出
3.5 GCD中有2个用来执行任务的函数
3.5.1 用同步的方式执行任务(在当前线程执行,不开启新线程)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:队列
block:任务
3.5.2 用异步的方式执行任务(会开启新线程)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
在这里需要注意一点:GCD的队列可分为2大类型
(1)并发队列(Concurrent Dispatch Queue),可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务),并发功能只能在异步(dispatch_async)函数下才有效.
(2) 串行队列(Serial Dispatch Queue),让任务一个接着一个执行(一个任务执行完毕后,再执行下一个任务).
注意:同步和异步主要影响能不能开启新的线程,同步在当前线程中执行,不具备开启新线程的能力;异步在新的线程中执行任务,具备开启新线程的能力。并发和串行主要影响任务的执行方式,并发是多个任务并发(同时)执行,串行是一个任务执行完毕后再执行下一个任务。
3.6 队列类型:(全局并发队列,串行队列,主队列)
全局并发队列:GCD默认已经提供了全局的并发队列,提供整个应用使用,不需要手动创建
获取全局并发队列的函数:dispatch_get_global_queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
参数1:全局并发队列的优先级(影响该队列的调度次数)
DISPATCH_QUEUE_PRIORITY_HIGH 2 高
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台
参数2:该参数暂时没有任何作用,传0
串行队列:GCD中获得串行队列有2种途径
途径1:使用dispatch_queue_create函数创建串行队列
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
参数1:队列名称;参数2:队列属性,一般用NULL即可
注:非ARC,需要释放创建的队列 dispatch_release(queue);
途径2:使用主队列(跟主线程相关的队列),主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行。获取主队:dispatch_get_main_queue()
3.7 GCD同步异步以及队列的组合方式:主要掌握以下第一种方式即可,其他的组合方式可以直接忽略
/** 组合类型一:
* async -- 并发队列(最常用)
* 会不会创建线程:会,一般同时开多条
* 任务的执行方式:并发执行
*/
- (void)asyncGlobalQueue
{
// 获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 将 任务 添加 全局队列 中去 异步 执行
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
}
/** 组合类型二:
* async -- 串行队列(有时候用)
* 会不会创建线程:会,一般只开1条线程
* 任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
*/
- (void)asyncSerialQueue
{
// 1.创建一个串行队列,手动的,队列名称为heyifu.queue
dispatch_queue_t queue = dispatch_queue_create("heyifu.queue", NULL);
// 2.将任务添加到串行队列中 异步 执行
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
// 3.非ARC,需要释放创建的队列
// dispatch_release(queue);
}
/**组合类型三:不会用到,这种方式组合是多余的,这样的效果与直接把任务放到主线程中调用是一样的
* sync -- 并发队列
* 会不会创建线程:不会
* 任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
* 并发队列失去了并发的功能
*/
- (void)syncGlobalQueue
{
// 获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 将 任务 添加到 全局并发队列 中 同步 执行
dispatch_sync(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
}
/**组合类型四:不会用到,这种方式组合是多余的,这样的效果与直接把任务放到主线程中调用是一样的
* sync -- 串行队列
* 会不会创建线程:不会
* 任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
*/
- (void)syncSerialQueue
{
// 创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("heyifu.queue", NULL);
// 将 任务 添加到 串行队列 中 同步 执行
dispatch_sync(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
}
/**组合类型五:
* async -- 主队列(很常用)不会开启新线程,一般用在线程间的通讯
*/
- (void)asyncMainQueue
{
// 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加 任务 到主队列中 异步 执行
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
}
/**
* sync -- 主队列(不能用---会卡死)
*/
- (void)syncMainQueue
{
NSLog(@"syncMainQueue----begin--");
// 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加 任务 到主队列中 异步 执行
dispatch_sync(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
NSLog(@"syncMainQueue----end--");
}
总结:
全局并发队列 | 手动创建串行队列 | 主队列 | ||
同步(sync) |
1.没有开启新线程 2.串行执行任务 |
1.没有开启新线程 2.串行执行任务 |
1.没有开启新线程 2.串行执行任务 |
|
异步(async) |
1.有开启新线程 2.并发执行任务 |
1.有开启新线程(只开1条) 2.串行执行任务 |
1.没有开启新线程 2.串行执行任务 |
注意:使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列
3.8 GCD 线程间的通讯(子线程执行完耗时操作,回到主线程)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"donwload---%@", [NSThread currentThread]);
// 1.子线程下载图片
NSURL *url = [NSURL URLWithString:@"http://img2.bdstatic.com/static/searchdetail/img/dragtip1_f3e8b97.png"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"setting---%@ %@", [NSThread currentThread], image);
[self.button setImage:image forState:UIControlStateNormal];
});
});
}
4、NSOperation
简介:基于CGD(底层是GCD),比GCD多了一些简单实用的功能,使用更加面向对象
语言:OC
线程生命周期:自动管理
使用频率:经常使用