简介
GCD全称Drand Central Dispatch
- 纯
c语言,提供了非常多强大的函数
什么是GCD?
将任务添加到队列,并且指定执行任务的函数
GCD的优势:
-
GCD是苹果公司为多核的并行运算提供的解决方案。 -
GCD会自动利用更多的CPU内核(比如双核、四核) -
GCD会自动管理线程周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想执行什么任务,并不需要编写任何线程管理代码
基础写法
//任务
dispatch_block_t block = ^{
NSLog(@"hello world");
};
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//异步函数
dispatch_async(queue, block);
这就是将任务添加到队列,并通过异步函数执行。
任务使用
block封装,任务的block没有参数也没有返回值。-
异步函数 dispatch_async
不用等待当前语句执行完毕,就可以执行下一条语句- 会开启线程执行
block任务,异步是多线程的代名词
- 会开启线程执行
-
同步函数 dispatch_sync
必须等到当前语句执行完毕,才可以执行下一条语句- 不会开启线程,
在当前线程中执行block任务
- 不会开启线程,
同步和异步耗能问题
同步函数
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函数
dispatch_sync(queue, ^{
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
打印结果:
0.000008
异步函数
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//异步函数
dispatch_async(queue, ^{
// NSLog(@"任务1");
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
0.000027
空任务时,同步函数耗时更少。
添加一个NSLog任务
- 同步函数
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函数
dispatch_sync(queue, ^{
NSLog(@"任务1");
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
打印结果
任务1
0.000099
- 异步函数
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//异步步函数
dispatch_async(queue, ^{
NSLog(@"任务1");
});
NSLog(@"%f",CFAbsoluteTimeGetCurrent() - time);
打印结果:
0.000025
任务1
一个简简单单的打印语句,同步函数耗费时间远大于异步函数。
同步和异步函数都会耗费时间,异步函数多用来解决并发和多线程问题。
队列
串行队列:按照任务指派的顺序来执行任务,前一个执行完,下一个才能执行
并行队列:能够同时执行一个或多个任务,执行任务的顺序不一定
切勿将串行队列、并行对队列和同步、异步混淆
函数与队列搭配

同步函数与串行队列
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函数
dispatch_sync(queue, ^{
NSLog(@"任务1");
sleep(2);
});
dispatch_sync(queue, ^{
NSLog(@"任务2");
});
dispatch_sync(queue, ^{
NSLog(@"任务3");
});
打印结果:
任务1
任务2
任务3
同步函数与并行队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函数
dispatch_sync(queue, ^{
NSLog(@"任务1");
sleep(2);
});
dispatch_sync(queue, ^{
NSLog(@"任务2");
});
dispatch_sync(queue, ^{
NSLog(@"任务3");
});
打印结果
任务1
任务2
任务3
异步函数与串行队列
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
//同步函数
dispatch_async(queue, ^{
NSLog(@"任务1");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任务2");
});
dispatch_async(queue, ^{
NSLog(@"任务3");
});
dispatch_async(queue, ^{
NSLog(@"任务4");
});
dispatch_async(queue, ^{
NSLog(@"任务5");
});
异步函数与并行队列
//并行队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步函数
dispatch_async(queue, ^{
NSLog(@"任务1");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任务2");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任务3");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任务4");
sleep(2);
});
dispatch_async(queue, ^{
NSLog(@"任务5");
sleep(2);
});
打印结果
任务2
任务1
任务3
任务4
任务5
面试题
- (void)viewDidLoad {
[super viewDidLoad];
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"任务1");
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_async(queue, ^{
NSLog(@"任务3");
});
NSLog(@"任务4");
});
NSLog(@"任务5");
}
异步函数和并发队列不会阻塞线程。
打印结果:
任务1
任务5
任务2
任务4
任务3
- 修改一个同步函数
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"任务1");
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_sync(queue, ^{
NSLog(@"任务3");
});
NSLog(@"任务4");
});
NSLog(@"任务5");
打印结果为:
任务1
任务5
任务2
任务3
任务4
- 将队列改为串行队列
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.ly", NULL);
NSLog(@"任务1");
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_sync(queue, ^{
NSLog(@"任务3");
});
NSLog(@"任务4");
});
NSLog(@"任务5");
打印结果:
任务1
任务5
任务2
崩溃

面试题2:
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"0");
dispatch_sync(queue, ^{
NSLog(@"7");
});
dispatch_sync(queue, ^{
NSLog(@"8");
});
dispatch_sync(queue, ^{
NSLog(@"9");
});
答案为:AC
3必须在0之前,0在789之前,123无序 789 无序
//A. 1230789
//B. 1237890
//C. 3120798
//D. 2137890
主队列与全局队列
主队列:
- 专门用来在主线程上调度任务的串行队列
- 不会开启线程
- 如果当前主线程有任务正在执行,那么无论主队列中被添加了什么任务,都不会被调度
-dispatch_get_main_queue()
全局队列
- 为了方便程序员的使用,苹果提供了全局队列
dispatch_get_global_queue(0, 0) - 全局队列是一个并发队列
- 在使用多线程开发时,如果对队列没有特殊要求,在执行异步任务的时候,可以直接使用全局队列
死锁现象
主线程因为你同步函数的原因先执行任务,主队列等待着主线程中的任务执行完毕,在执行自己的任务,主线程和主队列相互等待会造成死锁。
GCD用法
- 单例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"创建单例");
});
- 同步/异步函数
//同步函数
dispatch_sync(queue, ^{
NSLog(@"3-%@",[NSThread currentThread]);
});
//异步函数
dispatch_async(queue, ^{
NSLog(@"4-%@",[NSThread currentThread]);
});
- dispatch_source
- 其
CPU负荷小,尽量不占用系统资源 - 联结的优势
在任一线程上调用它的一个函数dispatch_source_merge_data后,会执行dispatch_source实现定义好的句柄(可以把句柄简单理解为一个block),这个过程叫做Custom event,用户事件。是dispatch_source支持的一种事件。
句柄是一种指向指针的指针,它指向的就是一个类或者结构,它和系统有很密切的关系。HINSTANCE(实例句柄)、HBITMAP(位图句柄)、HDC(设备表述句柄)、HICON(图标句柄)等。这其中还有一个通用句柄,就是HANDLE
dispatch_source_create 创建源
dispatch_source_set_event_handler 设置源事件回调
dispatch_source_merge_data 源事件设置数据
dispatch_source_get_data 获取源事件数据
dispatch_resume 继续
dispatch_suspend 挂起
简单例子
- (void)viewDidLoad {
[super viewDidLoad];
self.totalComplete = 0;
self.queue = dispatch_queue_create("ly.com", NULL);
// 不依赖 runloop
// 和下层内核 kenel-workloop交互
// DISPATCH_SOURCE_TYPE_TIMER 准确
self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(self.source, ^{
NSLog(@"%@",[NSThread currentThread]);
NSUInteger value = dispatch_source_get_data(self.source);
self.totalComplete += value;
NSLog(@"进度: %.2f",self.totalComplete/100.0);
self.progressView.progress = self.totalComplete/100.0;
});
self.isRunning = YES;
dispatch_resume(self.source);
}
- (IBAction)didClickStartOrPauseAction:(id)sender {
if (self.isRunning) {
dispatch_suspend(self.source);
dispatch_suspend(self.queue);
NSLog(@"已经暂停");
self.isRunning = NO;
[sender setTitle:@"暂停中.." forState:UIControlStateNormal];
}else{
dispatch_resume(self.source);
dispatch_resume(self.queue);
NSLog(@"已经执行了");
self.isRunning = YES;
[sender setTitle:@"执行中.." forState:UIControlStateNormal];
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"开始了");
for (int i= 0; i<100; i++) {
dispatch_async(self.queue, ^{
if (!self.isRunning) {
NSLog(@"已经暂停");
return;
}
sleep(1);
dispatch_source_merge_data(self.source, 1);
});
}
}
- 栅栏函数
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
NSLog(@"2-%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"3");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
1、2执行完才会执行3、4
栅栏函数,堵塞的是队列
保护数据写入
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
@synchronized (self) {
[self.mArray addObject:image];
}
});
}
多线程同时向数组中写入数据时,可能会出现问题,上面使用了同步锁。也可以使用栅栏函数:
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_barrier_async(concurrentQueue, ^{
[self.mArray addObject:image];
});
});
}
-
栅栏函数不能使用全局队列
将上面代码对列改为全局队列,运行会崩溃
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<5000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_barrier_async(concurrentQueue, ^{
[self.mArray addObject:image];
});
});
}
崩溃效果图:

原因:
栅栏函数主要是拦截队列,不让队列后面的任务执行。而系统的全局队列,不只是添加了我们写的任务,系统也会使用这个队列,添加任务;被拦截才导致崩溃
- 调度组 dispatch_group_t
__block int a, b;
dispatch_queue_t queue = dispatch_queue_create("com.ly", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
a = 3;
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
b = 4;
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"%d",a+b);
});
- 进组和出组必须成对存在
- 只有当
group中的任务都执行完毕才会走dispatch_group_notify里的方法 -
dispatch_group_async相当于dispatch_group_enter()和dispatch_group_leave()
- 延迟
- 线程通讯
- 信号量/锁
信号量的简单使用:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(2);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务1");
sleep(1);
NSLog(@"任务1完成");
});
//任务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);
});
信号量创建的大小只有2,所以1、2任务会一起执行;执行完毕之后才会执行3。
GCD不像NSOperation可以直接控制并发数,设置maxConcurrentOperationCount;它需要借助信号量来实现这一需求。