dispatch_once 仅执行一次 一般用于单例实现
实现方式一
#import "FaceManager.h"
static FaceManager *faceManager = nil;
@implementation FaceManager
+(instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
faceManager = [[FaceManager alloc] init];
});
return faceManager;
}
注意:
//防止 alloc init new 引起的错误
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
return [FaceManager sharedInstance];
}
//防止NSCopying 引起的错误
-(id)copyWithZone:(NSZone *)zone
{
return [FaceManager sharedInstance];
}
//防止NSMutableCopying 引起的错误
-(id)mutableCopyWithZone:(NSZone *)zone {
return [FaceManager sharedInstance];
}
底层实现原理:
val:标记任务的执行状态 dispatch_once_f:执行的任务
底层有一个监测任务是否执行过的标记
- 已经执行过就直接return
- 未执行过,则开始执行,执行完就更新标记已执行完通知更新标记状态
- 正在执行中,则等待,通过循环拿取执行状态,通过状态来判断是否执行任务
GCD栅栏函数
自定义并发 栅栏函数
- 栅栏前面的队列里的任务 > 栅栏里的任务 > 栅栏后面队列里的任务
打印结果: 5 23 4 6
全局并发 栅栏函数(无效),
- 栅栏 拦不住
全局并发异步
全局并发 同步
主队列是可以拦住的,大家自行验证
为什么全局并发队列拦不住呢?
- 1、全局并发队列处任务的函数没有判断栅栏函数相关的代码
- 2、如果全局并发队列被栅栏住,将会影响操作系统函数的一些调用,显然不合适
栅栏函数能够栅住任务的原因:通过队列的一个状态值控制任务的执行顺序
GCD dispatch_group 调度组的作用:通过组的一个状态值来控制任务的执行顺序 与栅栏函数不同的是 组里的任务可以按顺序,而栅栏是控制 栅栏前的任务和栅栏后的任务,,而前面和后面的多个任务是无法确定顺序的
反观上面栅栏函数 栅栏前面多个任务并发是没有顺序的
- 1、调度组 dispatch_group_async == dispatch_group_enter + dispatch_group_leave
- 2、dispatch_group_notify作为调度组的最终入口会判断ds_status的值也就是组中任务的状态,所有任务完成之后方可进入notify函数 3、dispatch_group_enter 调度组里面值 +1 和 dispatch_group_leave 调度组里面值 -1 且必须成对出现
GCD 信号量
- 1、创建信号量 create 创建可控制的线程的个数
- dispatch_semaphore_t semphor = dispatch_semaphore_create(2);
- 其中2表示控制的线程的个数
- 2、等待信号量 对信号量的值—1操作 值为0 阻塞线程 一直等待直到值 >=1 才继续往下走
- dispatch_semaphore_wait(semphor, DISPATCH_TIME_FOREVER);
- 3、signal 发送信号量 -- 基于信号量的值+1
- dispatch_semaphore_signal(semphor);
作用:
- 控制并发线程数量 信号量的值为0时,按照顺序执行 结果:1 2 3 4
信号量的值为1时,按照顺序执行 结果:顺序不确定
总结:
- 当初始化的信号量的值 为0 时能控制执行的顺序,
- 当初始化的信号量的值 >0 时能控制并发的线程的执行顺序
- 注意:wait和signal必须成对出现,否则会导致崩溃 举例:
dispatch_semaphore_t semphor = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semphor, DISPATCH_TIME_FOREVER);
那么为什么会崩溃呢?
- 报错到:_dispatch_semaphore_dispose,该函数是释放信号量的函数
- 结论 :signal 和wait 函数必须成对出现 如果初始值为0,则会一直等待 GCD dispatch_source实现
- 1、创建队列,
- 2、创建源,确定源的类型
- 3、启动 和暂停 设置倒计时