iOS 底层探索篇 —— GCD栅栏函数、信号量、调度组和Dispatch_Source

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

1. 栅栏函数

1.1 栅栏函数的作用

栅栏函数最直接的作用是 控制任务执行顺序,同步

  • dispatch_barrier_async 前面的任务执行完毕才会来到这里
  • dispatch_barrier_sync 作用相同,但是这个会堵塞线程,影响后面的任务执行
  • 栅栏函数在全局队列中是无效的
  • 非常重要的一点: 栅栏函数只能控制同一并发队列

这里的函数运行状况是,123456 起来是随机打印的,而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_1DISPATCH_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_leavedispatch_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_invoke,发现调用了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
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享