1. 概念
- GCD:Gtand Central Dispatch 中枢调度器
- GCD主要解决的是多核并行运算
- GCD是自动管理线程的生命周期的
1.1 任务
- 要执行的操作
- 同步函数:在当前线程中执行任务,
不具备
开启新线程的能力,立马在当前线程绕那个同步执行任务
- 异步函数:可以在新的线程中执行任务,
具备
开启新线程的能力
1.2 队列
- 用来存放任务
- 并发队列(异步函数才有效)
- 串行队列
1.3 一般使用情况
- 声明一个任务
- 将任务添加到队列中
- GCD会自动将
队列中的任务取出
,放到对应的线程
中去 - 任务要遵循队列的
FIFO
原则,先进先出,后进后出
- GCD会自动将
使用 | 并发队列 | 串行队列 | 主队列 |
---|---|---|---|
异步函数 | 【1】具备开启新线程的能力【2】开启新的线程,任务异步执行 | 【1】具备开启线程的能力【2】会开线程,开一条线程,队列中的任务是按照先后顺序执行 | 【1】不会开启新的线程 【2】串行执行 |
同步函数 | 【1】不会开启新的线程【2】串行执行任务 | 【1】不会开启新的线程【2】串行执行任务 | 【1】如果在主线程中执行会发生死锁 【2】在子线程可以 |
2. 异步函数并发队列
- 异步函数 :具备开启新线程的能力
- 异步函数 + 并发队列 ->开新线程,队列中的任务是异步执行
/*
参数一: C语言的字符串,标识符用来区分队列
参数二: 队列的类型
DISPATCH_QUEUE_CONCURRENT 并发队列
DISPATCH_QUEUE_SERIAL 串行队列
*/
//创建一个队列
dispatch_queue_t queue = dispatch_queue_create("www.cc.com", DISPATCH_QUEUE_CONCURRENT);
//声明任务
//第一个参数:队列
//要执行的任务
// dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
dispatch_async(queue, ^{
NSLog(@"111111,--%@",[NSThread currentThread]);
});
注意: 一个队列中可以.可以添加多个任务,CGD开启线程是不受控制的,不是说有多少个任务就开启多少条线程,具体的是系统内部决定的
3. 异步函数 串行队列
- 异步函数 :具备开启新线程的能力
- 异步函数 + 串行队列: 会开线程,开一条线程,队列中的任务是按照先后顺序执行
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("cc", DISPATCH_QUEUE_SERIAL);
//创建异步函数任务
dispatch_async(queue, ^{
NSLog(@"2222222--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3333--%@",[NSThread currentThread]);
});
其实上面的创建的并发队列,可以换成系统的
全局并发队列
,区别:上面是自己手动创建
的并发队列,下面是获取系统的
//获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4. 同步函数+并发队列
- 同步函数:不具备开启新线程的能力
- 同步函数+并发队列:不会开启子线程,串行执行任务
//创建队列
dispatch_queue_t queue = dispatch_queue_create("hhh", DISPATCH_QUEUE_CONCURRENT);
//创建任务
//同步函数
dispatch_sync(queue, ^{
NSLog(@"999--%@",[NSThread currentThread]);
});
// 999--<NSThread: 0x60000155a240>{number = 1, name = main}
5. 同步函数+串行队列
- 同步函数:不具备开启新线程的能力
- 同步函数+串行队列:不会开启新的线程
//创建队列
dispatch_queue_t queue = dispatch_queue_create("kkk", DISPATCH_QUEUE_SERIAL);
//同步函数
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
6.主队列
- 主队列是GCD自带的一种比较特殊的串行队列
- 在主队列中的任务,都是在主线程中执行的
- 获取主队列
dispatch_get_main_queue()
6.1异步函数+主队列
- 不会开启子线程
// 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//异步函数执行
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
6.2 同步函数 + 主队列
- 同步函数:不具备开启新线程能力,
立马执行
,如果这个任务没有执行完,不会执行下一个任务
- 产生:
死锁
// 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//同步函数执行
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
6.3 线程产生死锁
造成死锁的条件:
使用同步sync函数 往当前 串行( SERIAL)队列 添加任务, 会卡住当前任务造成死锁
线程死锁
dispatch_sync
同步:立马执行当前线程中的任务,执行完毕才能继续往下执行
FIFO原则:先进的先出
意思就是: 整个函数
viewdidload
执行完,打印333,才可以执行 同步的任务 打印222, 由于同步函数sync
要立马执行当前任务,打印222,才可以执行打印333,所以会产生死锁
//MARK:---线程产生死锁
- (void)GCDLock1{
//原则: 同步线程 要立马执行
// FIFO:先进先出的原则,那个任务 先进来 马哥现任县执行
//GCDLock1 在主队列中,要先执行完 这个线程
//dispatch_sync 队列要立马在当前线程执行, 所以会产生死锁
//打印人3333 要先执行完,才会执行 打印2222,
//然而 打印任务2222 要立马执行完,才会执行打印任务3333
NSLog(@"11111");
dispatch_queue_t que = dispatch_get_main_queue();
dispatch_sync(que, ^{
NSLog(@"22222");
});
NSLog(@"333333");
}
原因:
1,创建了一个 子线程
2.队列0 和 队列1 都在 子线程中执行
3.由于队列0 执行到 打印执行任务2时,遇到 同步函数,要立马执行函数内 队列1的任务, 此时队列0的打印任务4 还没有执行完, 所以要等打印任务4执行完,才可以执行 同步函数 队列1,造成了线程死锁
// MARK:---会产生死锁2
- (void)GCDLock3 {
//会产生死锁
// 原因: 1,创建了一个 子线程
// 2.队列0 和 队列1 都在 子线程中执行
// 3.由于队列0 执行到 打印执行任务2时,遇到 同步函数,要立马执行函数内 队列1的任务, 此时队列0的打印任务4 还没有执行完, 所以要等打印任务4执行完,才可以执行 同步函数 队列1,造成了线程死锁
NSLog(@"执行任务1");
//DISPATCH_QUEUE_SERIAL 串行队列
dispatch_queue_t que = dispatch_queue_create("myque", DISPATCH_QUEUE_SERIAL);
dispatch_async(que, ^{ // 线程 队列0
NSLog(@"执行任务2");
dispatch_sync(que, ^{ //线程 队列1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
7.线程之间的通信
- 只要是主队列一定是在主线程执行的
- (void)setup7 {
//1.获取全局并发队列
dispatch_queue_t globalQue = dispatch_get_global_queue(0, 0);
//异步函数执行
dispatch_async(globalQue, ^{
//URL
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559637616849&di=ac9047efc5fb0dbe9eb8d55e321ac4a5&imgtype=0&src=http%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FzfDDxwQnD2Qib4s4tN6tVjeD9TXIedpicIZvKjL1u6MfRicUeAx9t31HLBrh11bfxhbiakH0te22TicfmCZgbErp3Gw%2F640%3Fwx_fmt%3Djpeg"];
//回去二进制数据
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//g回到主队列s刷新界面
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主队列");
self.imgageView.image = image;
});
});
}
8. GCD任务加强
//任务加强
- (void)demo1{
//获取并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//包装
void(^task)(void) = ^{
dispatch_sync(queue, ^{
NSLog(@"1111---同步--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2222---异步步--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"33333---异步步--%@",[NSThread currentThread]);
});
};
//执行任务
dispatch_async(queue, task);
}
9.栅栏函数
栅栏函数是:保证次函数前面的执行完,然后执行自己的栅栏函数,然后在执行 函数后面的线程
注意:
1.全局并发队列,对栅栏函数没有作用
2.使用栅栏函数,必须是自己手动创建的并发队列
//MARK:---栅栏函数
- (void)testBary {
//群居并发队列
// dispatch_queue_t que = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//自己手动穿件的队列
dispatch_queue_t que = dispatch_queue_create("cc", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(que, ^{
NSLog(@"---%@---1----",[NSThread currentThread]);
});
dispatch_async(que, ^{
NSLog(@"---%@---2----",[NSThread currentThread]);
});
dispatch_barrier_async(que, ^{
NSLog(@"-栅栏函数--%@-------",[NSThread currentThread]);
});
dispatch_async(que, ^{
NSLog(@"---%@---3----",[NSThread currentThread]);
});
dispatch_async(que, ^{
NSLog(@"---%@---4----",[NSThread currentThread]);
});
}
10.同时执行函数 apply
我们知道
for
循环是 执行一次循环,在执行下一次循环
有时我们在开发会遇到,多个任务同时执行的问题,比如:同时copy图片到另外一个文件夹中,此时就会用到apply
同时执行函数
- (void)viewDidLoad {
[super viewDidLoad];
// DISPATCH_QUEUE_PRIORITY_DEFAULT 默认队列
dispatch_queue_t que = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同时执行 并发队列 ,执行10不同的任务
dispatch_apply(10, que, ^(size_t index) {
NSLog(@"--%zd--%@---",index,[NSThread currentThread]);
});
}
11.CGD 队列组 - group
- (void)demo1{
//创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//创建任务组
dispatch_group_t group = dispatch_group_create();
//异步
dispatch_group_async(group, queue, ^{
NSLog(@"AAAA");
});
//异步
dispatch_group_async(group, queue, ^{
NSLog(@"bbbb");
});
//异步
dispatch_group_async(group, queue, ^{
NSLog(@"CCCC");
});
// 就是保证上面的3个任务 执行完毕 之后才执行 dispatch_group_notify
//这个任务组完成的通知
dispatch_group_notify(group, queue, ^{
NSLog(@"所有的任务完成");
});
NSLog(@"11111");
}
12 .常用的函数
12.1一次性执行函数
//MARK:---一次性执行函数
- (void)testOnce{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"执行一次就b在执行了");
});
}
12.2 常用的代码执行 延迟操作
//MARK:---延迟执行
- (void)testDiplay{
//延迟2l秒执行
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
//延迟3秒执行
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延迟4秒执行");
});
}
- (void)run {
NSLog(@"奔跑");
}