OC 多线程之 GCD知识从基础到进阶 (1)
概念认知
基本概念:
GCD 全称 (Grand Center Dispatch),翻译成通俗中文“牛B的中心调度机制”
线程:执行任务调度的最小单位
任务:就是一段代码,GCD中就是block中的内容。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力
队列:用来存放任务的线性结构,采用 FIFO(先进先出)的原则。
进阶概念:
-
同步:
- 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行后续任务。
- 只能在当前线程中执行任务,不具备开启新线程的能力。
-
异步:
- 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
- 可以在新的线程中执行任务,具备开启新线程的能力。
串行队列: 队列中的任务 按添加顺序开始执行
并发队列: 队列中的任务可同时开始执行,可以开辟多个线程
主队列(等待主线程内的任务完成之后再利用主线程执行任务)
全局并发队列:本质上就是一个apple默认的并发队列
GCD 使用步骤
-
创建队列:
// 串行队列的创建方法 dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL); // 并发队列的创建方法 dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT); // 主队列的获取方法 dispatch_queue_t queue = dispatch_get_main_queue(); // 全局并发队列的获取方法 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-
创建任务:
// 同步执行任务创建方法 dispatch_sync(queue, ^{ // 这里放同步执行任务代码 }); // 异步执行任务创建方法 dispatch_async(queue, ^{ // 这里放异步执行任务代码 });
串行并发队列 + 同步异步组合
1.同步执行 + 并发队列
2.异步执行 + 并发队列
3.同步执行 + 串行队列
4.异步执行 + 串行队列
5.同步执行 + 主队列
6.异步执行 + 主队列
各种组合:
问题:
死锁:在
主线程
用主队列
执行同步任务
就会造成线程死锁。主队列中的任务一定是在主线程中执行的
全局并发队列中的任务可以开辟新线程,但不一定会开辟
GCD 线程间通信
// GCD的线程间的通信 : 常用代码
- (void)GCDDemo
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"正在努力下载... %@",[NSThread currentThread]);
// 如果下载结束回到主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"下载完成,正在更新UI... %@",[NSThread currentThread]);
});
});
}
GCD 的其他方法
GCD 栅栏方法:dispatch_barrier_async
对于栅栏方法的理解,我觉得下面的图片很形象:
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_barrier_async(queue, ^{
// 追加任务 barrier
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
虽然是异步任务 栅栏也能把这两个任务给有先后顺序的隔离开来。
GCD 延时执行方法:dispatch_after
这里需要注意的是,延迟的时间是指的 延迟将任务添加到队列中。所以这个时间并不是特别准确的
/**
* 延时执行方法 dispatch_after
*/
- (void)after {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncMain---begin");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0秒后异步追加任务代码到主队列,并开始执行
NSLog(@"after---%@",[NSThread currentThread]); // 打印当前线程
});
}
GCD 一次性代码(只执行一次):dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
GCD 快速迭代方法:dispatch_apply
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
GCD 队列组:dispatch_group
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"group---end");
});
GCD队列组监听:dispatch_group_notify
/*
* 业务异步串行执行,用GCD实现的四种方式
* 1、dispatch_group_notify(group, queue, ^{ });
* 2、dispatch_group_async(group, backQueue, ^{ });
* 3、dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 会有线程等待阻塞当前线程
* 4、信号量 = 0 实现;会有线程等待阻塞当前线程
*/
// 被监听的group
dispatch_group_t group = dispatch_group_create();
// 将要执行后续block代码的队列
dispatch_queue_t queue = dispatch_get_main_queue();
// ----------------- 监听group执行完毕 后,在queue队列执行block代码
dispatch_group_notify(group, queue, ^{
// 任务代码
});
// ---------------- 全局并发队列
dispatch_queue_t backQueue = dispatch_get_global_queue(0, 0);
// 向group中添加 在某个队列中执行的任务
dispatch_group_async(group, backQueue, ^{
// 追加的任务
});
/*
* 监听group执行完毕 之后在某个队列中执行某个任务
* 跟 向group中追加某个队列中执行的任务 也有类似作用,但是group不会有线程等待,只是会造成先后执行的效果,但是不会有线程等待
*/
// ------------ 在此处等待group中的任务执行完毕 然后 执行wait后面的代码,线程等待会阻塞当前线程
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// 创建信号量 初始信号量0 线程等待
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 主队列中执行异步任务
dispatch_async(queue, ^{
// 信号量+1
dispatch_semaphore_signal(semaphore);
});
// 信号量不为0时候-1,信号量=0时候线程等待
// 异步任务异步执行后 直接带了wait,信号量=0 ,线程等待,等异步任务完毕时 信号量+1 不为零,此时线程不再等待,开始执行下面代码,会阻塞当前线程;
dispatch_semaphore_wait(semaphore, 1);
/*
* semaphore = 0 时候 dispatch_semaphore_wait 等待信号量!=0,
* 跟 dispatch_group_wait 等待group执行完毕 有一点相似。
*/
GCD中任务增删:dispatch_group_enter、dispatch_group_leave
// 向任务组添加任务
dispatch_group_enter(group);
// 将要添加的任务
dispatch_async(queue, ^{
// 追加任务1
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
// 成对出现 任务组任务执行完毕 撤销任务
dispatch_group_leave(group);
});
// 等待group五秒钟
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)));
// 要么五秒内执行完毕走到这里;要么五秒后超时,group还未执行完 就走到了这里
NSLog(@"等待之后");
// 如果只有enter 没有leave那么线程会一直存在 等待group执行完毕
GCD信号量:dispatch_semaphore_t
-(void)semaphoreLockTest{
dispatch_semaphore_t semaphoreLock = dispatch_semaphore_create(1);
// 创建两个购票的串行队列
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(&*self)weakSelf = self;
// 第一个购票队列
dispatch_async(queue1, ^{
[weakSelf semaphoreGetTicket:semaphoreLock];
});
// 第二个购票队列
dispatch_async(queue2, ^{
[weakSelf semaphoreGetTicket:semaphoreLock];
});
}
-(void)semaphoreGetTicket:(dispatch_semaphore_t)semaphore{
while (1) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (YES) {
// 有票
// 模拟购票延时
[NSThread sleepForTimeInterval:0.2];
// 相当于解锁
dispatch_semaphore_signal(semaphore);
}else{
// 无票
// 相当于解锁
dispatch_semaphore_signal(semaphore);
break;
}
}
}
线程锁
本文参考文章: