GCD 全称Grand Central Dispatch,是纯C语言,它会自动挂哪里线程的生命周期。
多线程最基本的运行原理就是将任务添加到队列,并且指定执行任务的函数,如下:
//1:创建串行队列
dispatch_queue_t queue = dispatch_queue_create("QueueName", DISPATCH_QUEUE_SERIAL);
//2:创建任务
dispatch_block_t taskBlock = ^{
NSLog(@"%@",[NSThread currentThread]);
};
//3:利用函数把任务放入队列
dispatch_sync(queue, taskBlock);
队列分为串行队列和并行队列 ,函数分为 同步函数(dispatch_sync)和异步函数(dispatch_async),队列和函数类型的组合,决定了能都开辟新的线程
- 同步:必须等待当前语句执行完毕,才会执行下一条语句,不会开启线程。
- 异步:不用等待当前语句执行完毕就可以执行下一条任务。
- a、并行队列 + 异步函数 开启多条线程,不按顺序执行任务;
- b、串行队列 + 异步函数 开启一条新线程,按顺序执行任务;
- c、主队列 + 异步函数不开启新线程,按顺序执行任务
- d、并行队列 + 同步函数不开启新线程,按顺序执行任务;
- e、串行队列 + 同步函数不开启新线程,按顺序执行任务;
- f、主队列 + 同步函数会出现卡死现象!原因:循环等待,主队列的东西要等主线程执行完,又不能开线程,所以下面的任务要等上面的任务执行完,然后卡死
关于函数与队列的组合Demo,点击此处下载~
还有一种情况的死锁
执行顺序如图所示, 当执行到sync_block的时候,由于是一个同步函数调用的串行队列,同步函数必须要先执行它block 里边的任务3,但是串行队列FIFO原则,3必须等到 4执行后才能执行, 双方相互等待,形成死锁。
关于主队列死锁
NSLog(@"0");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1");
});
NSLog(@"2");
同步函数调用主队列(主队列不是串行队列但是类似串行队列),同样会造成死锁。
栅栏函数
当我们的任务有依赖关系的时候,比如任务1和2执行完毕后才能执行任务3和4,这时候我们可以用到这个函数——栅栏函数。其中 queue 是队列,block 是任务。
dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t blcok)
提交一个栅栏函数在同步执行中,它会等待栅栏函数执行完再去执行下一行代码,同步栅栏函数是在主线程中执行的。
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t blcok)
提交一个栅栏函数在异步执行中,它会立马返回开始执行下一行代码(不用等待任务执行完毕)。
前提: 必须是同一个队列(queue)
注意点: 栅栏函数不能封装异步网络请求,因为异步网络请求不能保证任务在同一个队列上。
共同点:
- 都会等待自己前边插入的队列的任务(1、2、3)先执行完。
- 都会等待他们自己的任务(barrier)执行完后再执行后的任务。
不同点
- dispatch_barrier_sync 需要等待自己的任务(barrier)结束之后,才会继续添加并执行写在barrier后边的任务,然后执行。
- dispatch_barrier_async将自己的任务(barrier)插入到队列之后,不用等待自己任务的结束,会继续把后边的任务添加到队列中,然后开始执行。
调度组
我们经常需要需要监听多个异步任务全部完成后的回调。此时需要用到调度组处理
注意点: 必须是同一个组group
场景:使用异步下载或者请求的时候,等多个任务都执行完时, dispatch_group_notify()监听统一的回调。
基本的调度组执行如下:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4 repeats:NO block:^(NSTimer * _Nonnull timer) {
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop]run];
});
dispatch_group_async(group, queue, ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer * _Nonnull timer) {
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop]run];
});
dispatch_group_wait(group, 5); // 最长等待时间
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"--5^^ %@",[NSThread currentThread]); // <= 5秒调用
});
调度组内部方法 enter - leave
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"--1^^ 执行完了,准备出组");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"--2^^ 执行完了,准备出组");
dispatch_group_leave(group);
});
dispatch_group_wait(group, 5);
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"--5^^ %@",[NSThread currentThread]);
});
dispatch_group_enter() 与 dispatch_group_leave 保证成对出现,
dispatch_group_leave 少于dispatch_group_enter(),将不会调用通知, 反之crash;
信号量
信号量是用在多线程并发的可控最大任务数量的操作,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作
// 创建一个信号,value:信号量
dispatch_semaphore_create(<#long value#>)
// 使某个信号的信号量+1
dispatch_semaphore_signal(<#dispatch_semaphore_t dsema#>)
// 某个信号进行等待或等待降低信号量 timeout:等待时间,永远等待为 DISPATCH_TIME_FOREVER
dispatch_semaphore_wait(<#dispatch_semaphore_t dsema#>, <#dispatch_time_t timeout#>)
正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); // 同时执行任务的数量,不超过2个
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务1");
sleep(1);
NSLog(@"任务1完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务2");
sleep(1);
NSLog(@"任务2完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务3");
sleep(1);
NSLog(@"任务3完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_source
关于source源的设置, 1、2、3 为创建, 4、5、6为设置。
dispatch_source_create
1、创建source源
dispatch_source_set_event_handler
2、设置回调
dispatch_source_merge_data
3、引发回调(绑定source, 带参data)
dispatch_source_get_data
4、获取source绑定的data
dispatch_suspend
5、挂起
dispatch_resume
6.继续