一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希望未来技术之巅上有你们也有我。
通过下面的两个方法可以证明:
代码如下(示例):
提示:这里对文章进行总结:
前言
正题
1.什么是线程
2.什么是串行队列和异步队列
串行队列或者异步队列只会控制任务是否按顺序执行.
串行队列是按顺序
执行的.
异步队列是不顺序
执行的
通过下面的两个方法可以证明:
串行队列-异步线程
- (void)gcdDemo2 {
//创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 200; ++i) {
dispatch_async(queue, ^{
// 异步执行
NSLog(@"%@ - %d",[NSThread currentThread],i);
});
}
}
答案:按顺序执行,只开一条线程,为什么?
因为在一个串行的队列里面添加了200个任务,每个任务都是在串行中需要排队执行,在这个串行队列里面,当前的任务没有执行完毕,下一个任务是不会开始执行的.即使现在开启了有多个异步线程number3,number4,number5…空闲着,在串行队列里面,也不会把任务放到number3,number4,number5里面去执行,实际也不会有number3,number4,number5等多个异步线程在空闲去浪费资源,因为线程空闲会被收回到可用线程池,但主线程是不会被收回.
并发队列-异步线程
答案:不按顺序执行,开多条线程
- (void)gcdDemo2 {
//创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 200; ++i) {
dispatch_async(queue, ^{
// 异步执行
NSLog(@"%@ - %d",[NSThread currentThread],i);
});
}
}
3.什么同步线程和异步线程
同步线程和异步线程只会控制是否开启线程
同步线程是不开线程
,在主线程中执行
异步线程是开线程
,不在主线程中执行
通过下面的两个方法可以证明:
有开启线程
//创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
// 异步
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
开启了异步线程,证明有开线程
没有开启线程
//创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
// 同步
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
程序本身在主线程里面运行
4.开启一个串行队列在异步线程中执行
OC-串行队列在异步线程中执行
按顺序执行,可以开线程
- (void)gcdDemo {
//创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 100; i++) {
// 异步
dispatch_async(queue, ^{
NSLog(@"%@, %d",[NSThread currentThread], i);
});
}
}
5.开启一个串行队列在同步线程中执行
OC- 串行队列在同步线程中执行
肯按顺序执行,不开线程
//创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 100; i++) {
// 异步
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
}
6.开启一个并发队列在异步线程中执行
OC-并发队列在异步线程中执行
不按顺序执行,可以开线程
- (void)gcdDemo {
//创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 100; i++) {
// 异步
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
}
}
7.开启一个并发队列在同步线程中执行
OC- 并发队列在同步线程中执行
不按顺序执行,不开线程
- (void)gcdDemo {
// 1. 创建一个并发队列
// DISPATCH_QUEUE_CONCURRENT 用于创建并发队列
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 100; i++) {
// 同步
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
}
}
8.开多少条线程又谁来决定?
开多少硬件或者软件决定
如果开启并发队列-异步线程执行200个任务,开多少条线程根据任务本身来决定的,例如,任务1下载一张图片,任务2下载一场电影,如果执行快的任务可以重新利用旧的线程,如果任务大就会开新的线程.
9.什么是主队列
OC- 主队列-示例代码
专门处理主线程中的任务,就是主队列,按顺序执行,先进先出.
添加到主队列中的任务是在主线程有空的时候才执行.
证明:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self gcdDemo];
NSLog(@"end");
}
-(void)gcdDemo {
// 1. 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 异步是可以先往下执行代码,再回来执行
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
}
上面的代码打印了end才去执行主队列的任务.说明等主队列有空的才去执行.
10.同步线程和异步线程里面执行方法的顺序关系
在一个方法里面异步线程里面的函数跟方法外面的函数是不按顺序执行的
在一个方法里面同步线程里面的函数跟方法外面的函数是按顺序执行的
11.死锁
产生死锁的原因是因为主队列的任务必须有空闲才执行,也就是执行函数1才去执行函数2,但是现在主队列必须是按顺序执行的,也就是执行函数2才去执行函数1.这样产生了矛盾.
如果非要使用主队列去执行的话解决办法看下面的代码:
12.同步的作用
保证先执行某些程序再去执行别的程序.
例如肯定先登录之后才能下载.
13.全局队列(跟并发一样,可以开多小线程)
OC- 全局队列-示例代码
- (void)gcdDemo {
//创建全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//异步线程中执行
for (int i = 0; i < 200; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ - %d",[NSThread currentThread], i);
});
}
}
重点,主队列执行耗时操作完回到主线程刷新UI
我们一般会在主队列中执行一下好事操作的代码,下载图片,然后下载完成之后回到主线程里面刷新UI
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"耗时操作");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程刷新UI");
});
});
14.barrier 异步(少用)
注意barrier,一定要使用异步,如果同步会产生死锁,因为barrier的代码是等待异步执行完才去执行barrier,如果使用同步,同步要barrier执行完才往下走,barrier也等同步执行完才往下走,造成互相等待,产生死锁
主要用于多个异步操作完成之后,统一对非线程安全的对象进行更新
看下面的代码,代码在下载(运行)过程中会然后不停的点击会出现奔溃的可能
也就是说这样把图片保存进去数组是不安全的,应该放到安全的地方去执行.为什么不安全呢?因为在异步下载图片的时候,把下载完的图片直接保存进去数据,也是在异步线程里面完成,如果点击屏幕的操作产生的时间差异,可以异步刚好下载两张图片保存在数组的同一个地方.
解决办法就是把图片下载完成的操作代码保存起来,等所有图片下载完成之后,在统一的线程里面保存图片
其实可以回到主线程保存图片
15.GCD延时操作的方法
OC- GCD延时-示例代码
参数一: 什么时候开始算起 DISPATCH_TIME_NOW 现在
参数二: 几秒钟之后执行代码。0.3秒后 (NSEC_PER_SEC)10亿纳秒 = 1秒
参数三: dispatch_get_main_queue()。延时之后在那个队列中执行 主队列,如果想在其他线程中执行可以传递其他的子线程进去
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//dosoming
});
参数三: dispatch_get_main_queue()。延时之后在那个队列中执行 主队列,如果想在其他线程中执行可以传递其他的子线程进去,例如看下面的代码
16.调度组
OC- 调度组-示例代码
假如做电商的项目里面首页加载数据,首先请求3个接口,等数据全部回来之后才去刷新UI,就可以使用下面的方法去做
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self gcdDemo2];
}
- (void)gcdDemo2 {
// 先创建一个组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 调度组
dispatch_group_async(group, queue, ^{
NSLog(@"下载文件A");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"下载文件B");
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载文件C");
});
// 注册通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"文件下载完成,刷新UI");
});
NSLog(@"end");
}
@end
主要的工作原理;当开启异步线程去做很多个任务的时候,没去做一个任务都会把它放到组里面去,然后没放一个任务都必须等待完成,移除组,如果没有移除,就等于没有完成,就不会有通知,添加多少个进组,就有多少个等待移除.所以只有全部移除就会发送通知.这样可以回到主线程刷新UI