参考文档1
参考文档2
参考文档3
参考文档4
参考文档5
Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。
1. 两种队列
两种队列都用dispatch_queue_create创建,传入两上参数,
第1个参数是队列唯一标识符,用于DEBUG,可为空。
第2个参数用于标识队列类型,串行/并发。
//串行队列的创建
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
//并发队列的创建
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
//主队列(Main Dispatch Queue)
//主队列是一种特殊的串行队列
**所有放在主队列中的任务,都会放到主线程中执行**
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//全局并行队列(Global Dispatch Queue)
//第1个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。
//第2个参数暂时没用,用0即可。
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
队列是指执行任务的等待队列,即用来存放任务的队列。
是一种特殊的线性表,采用 FIFO(先进先出)的原则,由GCD内部自行控制。
同步执行和异步执行 指的都是对一个已经创建的队列,把任务(代码/代码块)添加进去的方式。
同步执行和异步执行区别:
- 会不会阻塞当前线程,是否等待队列的任务执行结束
- 是否具备开启新线程的能力。
**
串行队列(Serial Dispatch Queue)
每次只执行一个任务,让任务一个接着一个地执行。一个任务执行完毕后,再执行下一个任务。
只开启一个新线程(主线程则不开启新线程,在当前线程执行任务)并发队列(Concurrent Dispatch Queue)
可以让多个任务并发(同时)执行。
可以开启多个线程,并且同时执行任务。同步执行(sync)
同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
只能在当前线程中执行任务,不具备开启新线程的能力。异步执行(async)
异步添加任务到指定的队列中,它不会做任何等待,
可以继续执行任务。
可以在新的线程中执行任务,具备开启新线程的能力。
2. 任务的创建
GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async。
// 同步执行任务创建方法
dispatch_sync(queue, ^{
// 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
// 这里放异步执行任务代码
});
1. 异步执行 + 并行队列
/**
* 异步执行 + 并行队列
* 特点:可以开启多个线程,任务交替(同时)执行。
*/
- (void)asyncConcurrent {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"asyncConcurrent---end");
}
结果 :
所有任务是在打印的
syncConcurrent---begin和syncConcurrent---end之后才执行的。
说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务
总结:
异步执行具备开启新线程的能力,所以除了当前主线程,系统又开启了其它线程,任务之间不需要排队,且是交替/同时执行的。
2. 异步执行 + 串行队列
/**
* 异步执行 + 串行队列
* 特点:会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。
*/
- (void)asyncSerial {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"asyncSerial---end");
}
结果
所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
总结
异步执行具备开启新线程的能力,但由于是串行队列所以只开启一个线程。任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)
3. 同步执行 + 并行队列
/**
* 同步执行 + 并行队列
* 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
**/
- (void)syncConcurrent {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"syncConcurrent---end");
}
结果 :
所有任务都在打印的syncConcurrent---begin
和syncConcurrent---end之间执行的(同步任务需要等待队列的任务执行结束),所有 任务按顺序执行的
总结:
所有任务都是在当前主线程中执行的,没有开启新的线程(同步执行不具备开启新线程的能力)。
按顺序执行的原因:虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为队列本身不能创建新线程,只有当前线程这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。
可见并发队列的并发功能只有在异步(dispatch_async)函数下才有效。
4. 同步执行 + 串行队列
/**
* 同步执行 + 串行队列
* 特点:不会开启新线程,在当前线程执行任务。
任务是串行的,执行完一个任务,再执行下一个任务。
*/
- (void)syncSerial {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"syncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"syncSerial---end");
}
结果
所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)
总结:
这里的执行原理和步骤图跟“同步执行+并发队列”是一样的,只要是同步执行就没法开启新的线程,所以多个任务之间也一样只能按顺序来执行
任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。
5. 同步执行+主队列
/**
* 同步执行 + 主队列
* 特点(主线程调用):互等卡主不执行。
* 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。
*/
- (void)syncMain {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"syncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"syncMain---end");
}
总结:
主线程中同步执行+主队列
在主线程中执行syncMain方法,相当于把syncMain任务放到了主线程的队列中。而同步执行会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务1追加到主队列中,任务1就在等待主线程处理完syncMain任务。而syncMain任务需要等待任务1执行完毕,才能接着执行。
现在的情况就是syncMain任务和任务1都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且syncMain---end也没有打印。
6.异步执行 + 主队列
/**
* 异步执行 + 主队列
* 特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务
*/
- (void)asyncMain {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"asyncMain---end");
}
结果
所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)
总结:
所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中)。
任务是按顺序执行的(因为主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。
任务依赖
一般情况下,异步添加到并行队列中的任务,我们是没法控制执行顺序的,或者说也是按FIFO顺序执行,但执行完成的顺序确无法控制。下面是二般情况:
1.Barrier
barrier只能在自己创建的串行/并行队列中才有效
dispatch_queue_t globalQueue = dispatch_queue_create("ConcurrentQ", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(globalQueue, ^{
NSLog(@"barrier之前的任务1");
});
dispatch_async(globalQueue, ^{
NSLog(@"barrier之前的任务2");
});
/***************************/
//使用dispatch_barrier将任务添加到队列后,
//任务会在前面任务全部执行完后执行
//且任务在执行过程中,其它任务无法执行
dispatch_barrier_async(globalQueue, ^{
NSLog(@"barrier ConQ 1");
});
/***************************/
dispatch_async(globalQueue, ^{
NSLog(@"barrier之后的任务1");
});
dispatch_async(globalQueue, ^{
NSLog(@"barrier之后的任务2");
});
/***************************/
dispatch_barrier_async(globalQueue, ^{
NSLog(@"barrier ConQ 2");
});
dispatch_barrier_async(globalQueue, ^{
NSLog(@"barrier ConQ 3");
});
//由于是异步添加任务,所以不用等任务执行完,
//有可能会被首先执行打印
NSLog(@"当前队列的最后一个任务");
2.GCD group
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
---------------dipatch_group------------
//barrier将任务添加到一个队列中,可以同或异步添加
//group将伤添加到一个组当中,再将组放入队列,异步添加
dispatch_group_async(group, globalQueue, ^{
NSLog(@"globalQueue1");
});
dispatch_group_async(group, globalQueue, ^{
// sleep是全局休眠定时器
// group_wait只阻塞当前group block,所以3会先执行
dispatch_group_wait(gp, dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC));
NSLog(@"globalQueue2");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"globalQueue3");
});
/*************当group所有任务完成后,会发送完成通知******************/
dispatch_group_notify(group, globalQueue, ^{
NSLog(@"1.2.3 已完成");
});
//由于是异步添加到并行队列,这个任务有可能被最先执行完
dispatch_async(globalQueue, ^{
NSLog(@"globalQueue4");
});
或者
项目需求是AB进行网络请求,数据返回后,回主线程合并,但对网络请求而言,请求发出即算执行完毕。所以上面的写法就不更适合。
dispatch_group_t group = dispatch_group_create();
---------------dipatch_group------------
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任务一完成");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任务二完成");
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"任务完成");
});
网络请求开始前dispatch_group_enter(group) ,请求成功或失败后都执行dispatch_group_leave(group)。当所有的enter都leave后会触发通知。
3.Semaphore
信号量大于0时执行后面代码,等于0时等待
dispatch_semaphore_create 创建一个semaphore
dispatch_semaphore_signal 发送一个信号,让信号总量+1
dispatch_semaphore_wait 信号量为0时,等待信号;信号量大于0时,让信号量-1,并执行后面代码。
//创建信号量
dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
//添加任务1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"第一个任务");
dispatch_semaphore_signal(semaphore1);//信号量+1
});
//添加任务2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//判断信号量
//如果为0,则根据传入的时间等待
//如果不为0,则-1执行
dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
NSLog(@"第二个任务");
dispatch_semaphore_signal(semaphore2);//信号量+1
});
///判断信号量
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
NSLog(@"这是最后一个任务");
});
在网络请求中所有请求加异步加入到并行队列后,成功/失败都要semaphore_signal,然后在异步并行队列中判断是否可以执行
dispatch_queue_t queue = dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
dispatch_async(queue,^{
//第一个网络请求
... ...
dispatch_semaphore_signal(semaphore1);
}
dispatch_async(queue,^{
//第二个网络请求
... ...
dispatch_semaphore_signal(semaphore2);
}
//semphore_wait并发队列中执行
dispatch_async(queue,^{
dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
//刷新UI
});
});
PS:
dispatch_after是延迟提交,不是延迟运行,所以在串行队列中,先提交先运行。
//串行队列
dispatch_queue_t queue = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
NSLog(@"Begin add block...");
//提交一个block
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:10];
NSLog(@"First block done...");
});
//5 秒以后提交block
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
//dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
//dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);同样表示1秒
NSLog(@"After...");
});