进程
系统中正在运行的应用程序,如微信,支付宝都是一个进程,进程之间是相互独立的
线程
线程是进程执行的基本单元,一个进程上面的所有任务都是在线程上面执行的,程序启动默认开发一条主线程
队列
Dispatch Queue:用来存放任务的队列,队列是一种特殊的线性表,采用FIFO(先进先出)的原则
GCD队列类型
主队列
dispatch_get_main_queue
全局并发队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
自定义队列
串行队列
dispatch_queue_create("com.mr",DISPATCH_QUEUE_SERIAL);
并发队列
dispatch_queue_create("com.mr",DISPATCH_QUEUE_CONCURRENT);
队列和线程的关系
- 队列负责任务调度
- 线程负责任务执行
任务
任务就是执行操作的意思,也就是在线程中执行的那段代码,在GCD中是放在block中的,执行任务有两种方式:
- 同步执行(sync)
无开子线程能力:在添加的任务执行结束之前,会一直等待 - 异步执行(async)
有开子线程能力:线程会立即返回,无需等待就会继续执行下面的任务,不会阻塞当前线程 总结:dispatch_async(异步)和dispatch_sync(同步):决定有没有开子线程的能力,而不是指会不会开子线程。queue 决定有没有并发的能力。开子线程就并发,不开就串行。
同步串行
dispatch_queue_t serial_queue = dispatch_queue_create("com.mr",DISPATCH_QUEUE_SERIAL);
dispatch_sync(serial_queue,^{//任务});
异步串行
dispatch_queue_t serial_queue = dispatch_queue_create("com.mr",DISPATCH_QUEUE_SERIAL);
dispatch_async(serial_queue,^{//任务});
同步并发
dispatch_queue_t queue = dispatch_queue_create("com.mr",DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue,^{//任务});
异步并发
dispatch_queue_t queue = dispatch_queue_create("com.mr",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,^{//任务});
GCD面试题
同步遇到了串行
-(void)viewDidLoad {
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"1");// 任务1
dispatch_sync(queue, ^{
NSLog(@"2");// 任务2
});
NSLog(@"3");// 任务3
}
对于这块产生死锁的原因,网上教程很多,最多的一个是如下: 首先执行任务1,这是肯定没有问题的,接下来,程序遇到了同步线程,那么它会进入等待,等待任务2执行完,在执行任务3,但是这是主队列,是一个特殊的串行队列,有任务来,就会将任务添加到队尾,遵循先进先出的原则,那么,现在任务2就会被加到最后,任务3排在任务2的前面,导致死锁。
个人感觉上面的分析是错误的,我们可以用代码直接证明,去掉任务3,如下:我们发现还是产生了死锁
-(void)viewDidLoad {
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"1");// 任务1
dispatch_sync(queue, ^{
NSLog(@"2");// 任务2
});
}
我这边的理解是:代码在viewDidLoad中执行,NSLog(@"1")正常输出,之后遇到dispatch_sync,这个代码在 主线程执行,同时block任务也是主线程中执行,主线程是串行队列,先进先出的原则,dispatch_sync先加入 block任务后加入,所以先执行dispatch_sync,后执行block任务,但是dispatch_sync必须等待block执行 完成,才会执行,导致了相互等待,最后死锁,这只是个人理解,还望对这块理解深刻的大佬指点~
同步遇到了并行
-(void)viewDidLoad {
NSLog(@"1"); // 任务1
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"2"); // 任务2
});
NSLog(@"3"); // 任务3
}
输出结果
1
2
3
这个很好理解,首先执行任务1,接下来一个并行队列同步执行,输出2,最后输出3
同步异步都有
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.mr", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); // 任务1
dispatch_async(queue, ^{
NSLog(@"2"); // 任务2
dispatch_sync(queue, ^{
NSLog(@"3"); // 任务3
});
NSLog(@"4"); // 任务4
});
NSLog(@"5"); // 任务5
//1 5 2
}
任务2 任务3 任务4这里产生死锁,和第一个例子同理,第一个例子是在主线程中,这里是在一个子线程中
异步遇到同步回主线程
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1"); // 任务1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2"); // 任务2
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3"); // 任务3
});
NSLog(@"4"); // 任务4
});
NSLog(@"5"); // 任务5
//1 5 2 3 4
}
GCD的线程同步方案
栅栏函数
我们在并发异步函数的时候,我们想要先执行完某一个或者几个任务之后在执行下面的任务,我们就可以使用栅栏函数,栅栏函数传入的队列必须是dispatch_queue_create创建的使用dispatch_get_global_queue会有问题
上图箭头就是栅栏函数,我们想要执行完任务一和任务二之后在执行任务三
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSMutableArray *arr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.arr = [[NSMutableArray alloc]initWithObjects:@"1",@"2",@"3", nil];
dispatch_queue_t queue = dispatch_queue_create("com.mr", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//任务1
NSLog(@"%@",self.arr);
});
dispatch_async(queue, ^{
//任务2
NSLog(@"%@",self.arr);
});
dispatch_barrier_async(queue, ^{
[self.arr addObject:@"4"];
[self.arr addObject:@"5"];
});
dispatch_async(queue, ^{
//任务3
NSLog(@"%@",self.arr);
});
}
@end
输出如下:
2022-02-15 18:55:48.759533+0800 OCProject[24114:375362] (
1
)
2022-02-15 18:55:48.759538+0800 OCProject[24114:375357] (
1
)
2022-02-15 18:55:48.759741+0800 OCProject[24114:375357] (
1,
2,
3
)
调度组
主要用与线程同步,比如多个异步网络请求,全部请求完成进行汇总处理
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSMutableArray *arr;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block NSInteger num = 0;
dispatch_group_t group = dispatch_group_create();
//耗时操作1
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
NSLog(@"耗时操作完成");
num = num + 10;
[self.arr addObject:@"2"];
});
//网络请求1
dispatch_group_enter(group);
[self sendRequestWithComplention:^(id response) { NSLog(@"网络请求1完成"); num = num + 5; dispatch_group_leave(group); }];
//网络请求2
dispatch_group_enter(group);
[self sendRequestWithComplention:^(id response) { NSLog(@"网络请求2完成"); num = num + 3; dispatch_group_leave(group); }];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"%ld",(long)num);
});
}
-(void)sendRequestWithComplention:(void (^)(id response))completion{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
sleep(1);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(@"完成");
}
});
});
}
@end
输出如下,达到了异步线程执行网络请求,最后汇总执行结果的目的
OCProject[24664:403262] 网络请求2完成
OCProject[24664:403262] 网络请求1完成
OCProject[24664:403405] 耗时操作完成
OCProject[24664:403262] 18
信号量
作用:控制GCD最大并发数,以及控制GCD执行顺序
- 控制最大并发数
dispatch_semaphore_t sem = dispatch_semaphore_create(2);
dispatch_queue_t queue = dispatch_queue_create("com.mr", DISPATCH_QUEUE_CONCURRENT);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务1");
sleep(1);
NSLog(@"任务1执行完成");
dispatch_semaphore_signal(sem);
});
//任务2
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务2");
sleep(1);
NSLog(@"任务2执行完成");
dispatch_semaphore_signal(sem);
});
//任务3
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务3");
sleep(1);
NSLog(@"任务3执行完成");
dispatch_semaphore_signal(sem);
});
//任务4
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务4");
sleep(1);
NSLog(@"任务4执行完成");
dispatch_semaphore_signal(sem);
});
控制最大并发数为2,每次最多执行2个任务
2022-02-15 22:51:35.298210+0800 GCD[5128:74679] 执行任务1
2022-02-15 22:51:35.298210+0800 GCD[5128:74680] 执行任务2
2022-02-15 22:51:36.302372+0800 GCD[5128:74680] 任务2执行完成
2022-02-15 22:51:36.302372+0800 GCD[5128:74679] 任务1执行完成
2022-02-15 22:51:36.302743+0800 GCD[5128:74682] 执行任务3
2022-02-15 22:51:36.302743+0800 GCD[5128:74681] 执行任务4
2022-02-15 22:51:37.306466+0800 GCD[5128:74682] 任务3执行完成
2022-02-15 22:51:37.306466+0800 GCD[5128:74681] 任务4执行完成
- 控制执行顺序
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("com.mr", DISPATCH_QUEUE_CONCURRENT);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务1");
NSLog(@"任务1执行完成");
});
//任务2
dispatch_async(queue, ^{
NSLog(@"执行任务2");
sleep(1);
NSLog(@"任务2执行完成");
dispatch_semaphore_signal(sem);
});
控制先执行任务2,再执行任务1
2022-02-15 22:54:59.704199+0800 GCD[5303:77593] 执行任务2
2022-02-15 22:55:00.709321+0800 GCD[5303:77593] 任务2执行完成
2022-02-15 22:55:00.709667+0800 GCD[5303:77596] 执行任务1
2022-02-15 22:55:00.709875+0800 GCD[5303:77596] 任务1执行完成
总结:
- dispatch_semaphore_create 创建一个semaphore
- dispatch_semaphore_signal 发送一个信号
- dispatch_semaphore_wait 等待信号 dispatch_semaphore_signal是发送一个信号,会让信号总量加1,dispatch_semaphore_wait等待信号,会让信号量减1,当信号总量少于0的时候就会一直等待,否则就可以正常的执行