这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战
1. 栅栏函数
1.1 栅栏函数的作用
栅栏函数
最直接的作用是控制任务执行顺序,同步
。
dispatch_barrier_async
前面的任务执行完毕才会来到这里dispatch_barrier_sync
作用相同,但是这个会堵塞线程,影响后面的任务执行- 栅栏函数在
全局队列中是无效的
- 非常重要的一点: 栅栏函数只能控制
同一并发队列
这里的函数运行状况是,123
, 456
和起来
是随机打印的,而dispatch_barrier_async里面的函数
和加载那么多喘口气
是一定在456后面
运行的。
如果是dispatch_barrier_sync
,那么就会堵塞线程
,影响后面的任务执行,也就是说会影响起来
的执行和加载那么多
的执行,这两个任务会在栅栏函数执行完后执行,执行顺序不固定。
如果栅栏函数和其他函数不在同一队列,那么就不会影响其他队列任务的执行
。
如果是全局队列的话,那么栅栏函数也是无效的
。
栅栏函数还能起到锁
的作用。
数组是线程不安全
的,这样异步向数组添加元素的话是会崩溃的。在多线程条件下,mArray的地址永远是同一个
,在执行过程中,mArray元素的个数不停的变化。在添加元素的时候,需要对mArray进行一个读的操作,然后在进行一个写的操作
。而写的操作就会有对新值的retain和对旧值的release
。这个时候如果有一个多线程进行同时对一个位置写的操作,也就是对同一时间对同一内存空间进行操作
,那么就会不安全就会报出异常。
但是如果加入栅栏函数的话,就不会崩溃了。
1.2 栅栏函数的原理
栅栏函数是如何实现的呢?
看到dispatch_barrier_sync
会调用_dispatch_barrier_sync_f
。
接着跳转_dispatch_barrier_sync_f_inline
。
这里来到_dispatch_barrier_sync_f_inline
。
然后看到_dispatch_sync_recurse
。这里进行死循环递归,确保之前的任务已经清空了,然后进行自己block的调用.
接着看_dispatch_sync_invoke_and_complete_recurse
。
然后来到_dispatch_sync_complete_recurse
。这里判断是否存在barrier
,如果存在,就调用dx_wake
将队列的任务进行唤起执行,然后才调用_dispatch_lane_non_barrier_complete
进行状态的修改。
查找一下dx_wake 也就是dq_wake。普通的队列是_dispatch_lane_wakeup
,全局队列则是_dispatch_root_queue_wakeup
。_dispatch_lane_wakeup中如果是barrier形式,就会调用_dispatch_lane_barrier_complete
,否则就调用_dispatch_queue_wakeup
。而全局队列则没有对barrier进行处理,也就是说barrier对全局队列没有影响
。全局队列对栅栏函数不处理的原因是,全局队列不仅仅被我们使用,也被系统使用。如果对栅栏函数进行处理,那么可能影响到系统任务的执行,
所以全局队列不对barrier进行处理。
看到_dispatch_lane_barrier_complete
,发现如果是串行队列的话,就等待。否则就把栅栏去掉。
接下来看_dispatch_lane_class_barrier_complete。这里就是把之前的任务都清空了,这样就没有栅栏了,这样下次进来的话,剩下的任务进来就可以执行了。
2. 信号量
2.1 信号量作用
- dispatch_semaphore_create创建信号量
- dispatch_semaphore_wait 信号量等待
- dispatch_semaphore_signal信号量释放
- 控制GCD最大并发
- 同步->当锁
这个例子里,控制了最大的并发量为2,那么就会任务1,任务2先执行,当有dispatch_semaphore_signal
执行之后,才会执行任务3 和任务4 中的一个,保持最大并发量为2的状态。
下面这种情况的话,2会比1先执行。这里的信号量为0,这时候加了一个wait,那么就会进入一个等待的状态,在任务2完成了之后,dispatch_semaphore_signal发送信号,那么1就可以执行了。
2.2 信号量原理
先看dispatch_semaphore_signal
。这里进行了一个++的操作,并且当value大于0
的时候,返回0
。如果dispatch_semaphore_wait
太多,那么就会报错。如果值不等于LONG_MIN
,那么就会调用_dispatch_semaphore_signal_slow
。
_dispatch_semaphore_signal_slow会进行_dispatch_sema4_create
和_dispatch_sema4_signal
的调用。
_dispatch_sema4_signal
也就相当于一个do while循环
。
再来看dispatch_semaphore_create
。看到这里写的参数如果小于0就返回,说明0的话是可以正常运行
的,小于0就不能。
在看到dispatch_semaphore_wait
,这里有--
操作,并且大于等于0才返回0。所以当信号量为0时,调用dispatch_semaphore_wait之后,value = -1 < 0就无法正常执行了,就走到_dispatch_semaphore_wait_slow
里面。
_dispatch_semaphore_wait_slow
里面根据外面传进来的timeout
进行不同的处理。
默认的话就跳出去,DISPATCH_TIME_NOW的话就及时发送一个超时处理,DISPATCH_TIME_FOREVER的话就等待
。
看到_dispatch_sema4_wait
里面做了do while循环
。
那么这个函数里的dispatch_semaphore_wait,也就是相当于一个do while循环,这也是为什么后面的代码不执行的原因。
3. 调度组
3.1 调度组作用
- 最直接的作用: 控制任务执行顺序
- dispatch_group_create 创建组
- dispatch_group_async 进组任务
- dispatch_group_notify 进组任务执行完毕通知
- dispatch_group_wait 进组任务执行等待时间
- dispatch_group_enter 进组
- dispatch_group_leave 出组
- dispatch_group_async = dispatch_group_enter + dispatch_group_leave
- 注意搭配使用,进组和出租
成对
,并且先进后出
下面这段代码,dispatch_group_notify
会等待任务1和任务2执行完,在执行。
3.2 调度组原理
接下来我们要去底层探索
- 为什么先出组会崩溃
- 为什么 dispatch_group_async = dispatch_group_enter + dispatch_group_leave
- 为什么dispatch_group_notify会等待之前的任务出组后在执行。
先来看dispatch_group_create
,看到实际调用_dispatch_group_create_with_count
。
这里传过来的参数为0。这里为0的话,就会直接返回dg。
接下来看dispatch_group_enter
。这里做了--
的操作,
再来看一下dispatch_group_leave
。这里做了++
的操作,如果old_value == DISPATCH_GROUP_VALUE_1
的时候就会进行状态的处理。而要old_value等于DISPATCH_GROUP_VALUE_1,(old_state & DISPATCH_GROUP_VALUE_MASK)就要为DISPATCH_GROUP_VALUE_1
,DISPATCH_GROUP_VALUE_MASK
是一个很大的值
,那么old_state
就要为-1
才会等于DISPATCH_GROUP_VALUE_1
。而当old_value为0的时候,那么old_state就要为0
,就会报错
。这也是为什么先调用dispatch_group_leave就会报错
。这里面的_dispatch_group_wake
是唤醒执行dispatch_group_notify
。
接下来看一下dispatch_group_notify
。这里当old_state
为0时,那么就会唤醒
,否则就唤醒不了。
dispatch_group_leave
和 dispatch_group_notify
里面都有_dispatch_group_wake
去唤醒执行dispatch_group_notify
有的原因是当执行到dispatch_group_notify
的时候,有可能之前的任务已经执行完了
,然后dispatch_group_enter 和 dispatch_group_leave已经成对出现
了,这时候old_state 为0
就会直接执行dispatch_group_notify的block
。还有一种可能是执行到dispatch_group_notify
的时候,dispatch_group_leave
卡在异步执行的block里面,还没有执行,那么这时候就需要把dispatch_group_notify里的任务封装给group
,然后等到dispatch_group_leave执行到old_state为-1的时候,就会去执行唤醒group
的操作,并执行之前封装好的任务
。所以这里面的两个唤醒操作主要是为了防止多线程的影响
。
在看一下dispatch_group_async
。
继续来到_dispatch_continuation_group_async
,这里有一个dispatch_group_enter
的操作。
在来看_dispatch_continuation_async
。
看到调用了dx_push也就是dq_push
。
根据之前的文章分析,_dispatch_root_queue_push
会走到。_dispatch_continuation_invoke_inline
,看到这里有对group进行操作。
进来看_dispatch_continuation_with_group_invok
e,发现调用了dispatch_group_leave
。这就是为什么dispatch_group_async = dispatch_group_enter + dispatch_group_leave
。
4. Dispatch_Source
4.1 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 挂起
4.2 Dispatch_Source timer
#define LSLock(...) \
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); \
__VA_ARGS__; \
dispatch_semaphore_signal(_semaphore);
@interface LSTimer() {
/// YES 重复执行,NO 相反
BOOL _repeats;
/// 定时器时间间隔
NSTimeInterval _timeInterval;
/// YES 定时器有效,NO 相反
BOOL _valid;
/// 传递的数据
id _userInfo;
/// 接收者 用__weak主要防止循环引用
__weak id _target;
/// 执行任务的方法
SEL _selector;
/// source定时器
dispatch_source_t _sourceTimer;
/// 信号量
dispatch_semaphore_t _semaphore;
/// 标志定时器是否正在运行,YES是,NO 相反
BOOL _running;
/// 队列
dispatch_queue_t _queue;
// 队列类型
ROTimerQueueType _queueType;
}
@end
@implementation LSTimer
#pragma mark 公开方法
/// 创建定时器
/// @param ti 时间间隔
/// @param aTarget 接收
/// @param aSelector 方法
/// @param userInfo 数据
/// @param yesOrNo 是否重复
/// @param queueType 队列类型
+ (LSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(nullable id)userInfo
repeats:(BOOL)yesOrNo
queueType:(ROTimerQueueType)queueType {
return [[LSTimer alloc] initTimerWithDelayTime:0 timeInterval:ti target:aTarget selector:aSelector userInfo:userInfo repeats:yesOrNo queueType:(ROTimerQueueType)queueType];
}
/// 创建定时器,自带回调函数
/// @param ti 时间间隔
/// @param yesOrNo 是否重复
/// @param queueType 队列类型
/// @param block 回调
+ (LSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
repeats:(BOOL)yesOrNo
queueType:(ROTimerQueueType)queueType
block:(void (^)(LSTimer *timer))block {
return [[LSTimer alloc] initTimerWithDelayTime:0 timeInterval:ti target:self selector:@selector(execTaskWithTimer:) userInfo:[block copy] repeats:yesOrNo
queueType:(ROTimerQueueType)queueType];
}
/// 恢复定时器
- (void)resume {
if (_running) return;
if (_sourceTimer) {
dispatch_resume(_sourceTimer);
_running = YES;
}
}
// 暂停定时器
- (void)suspend {
if (!_running) return;
if (_sourceTimer) {
dispatch_suspend(_sourceTimer);
_running = NO;
}
}
/// 销毁定时器
- (void)invalidate {
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
if (_valid) {
if (!_running){
dispatch_resume(_sourceTimer);
}
dispatch_source_cancel(_sourceTimer);
_sourceTimer = nil;
_target = nil;
_userInfo = nil;
_valid = NO;
}
dispatch_semaphore_signal(_semaphore);
}
#pragma mark 私有方法
- (instancetype)initTimerWithDelayTime:(NSTimeInterval)delayTime
timeInterval:(NSTimeInterval)ti
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(nullable id)userInfo
repeats:(BOOL)yesOrNo
queueType:(ROTimerQueueType)queueType {
self = [super init];
if (self) {
_queueType = queueType;
_valid = YES;
_timeInterval = ti;
_target = aTarget;
_selector = aSelector;
_userInfo = userInfo;
_repeats = yesOrNo;
switch (_queueType) {
case ROTIMER_QUEUE_MAIN_QUEUE:
_queue = dispatch_get_main_queue();
break;
case ROTIMER_QUEUE_GLOBAL_QUEUE:
_queue = dispatch_get_global_queue(0, 0);
break;
default:
break;
}
_semaphore = dispatch_semaphore_create(1);
__weak typeof(self)weakSelf = self;
_sourceTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
dispatch_source_set_timer(_sourceTimer, dispatch_time(DISPATCH_TIME_NOW, delayTime * NSEC_PER_SEC), _timeInterval * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(_sourceTimer, ^{
[weakSelf fire];
});
}
return self;
}
/// 启动
- (void)fire {
if (!_valid) return;
LSLock(id target = _target;)
if (!target) {
[self invalidate];
}
else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[target performSelector:_selector withObject:self];
#pragma clang diagnostic pop
if (!_repeats) {
[self invalidate];
}
}
}
/// 定时器任务回调
/// @param timer 定时器
+ (void)execTaskWithTimer:(LSTimer *)timer {
dispatch_async(dispatch_get_main_queue(), ^{
void (^block)(LSTimer *) = [timer userInfo];
if (block) block(timer);
});
}
- (id)userInfo {
LSLock(id ui = _userInfo) return ui;
}
- (BOOL)repeats {
LSLock(BOOL re = _repeats) return re;
}
- (NSTimeInterval)timeInterval {
LSLock(NSTimeInterval ti = _timeInterval) return ti;
}
- (BOOL)isValid {
LSLock(BOOL va = _valid) return va;
}
- (void)dealloc {
[self invalidate];
}
@end
复制代码