GCD源码分析

  • 常见数据结构

    • dispatch_object_t
      GCD全部类的基类 image.png 从源码能够看出dispatch_object_t是一个联合体(并且是一个透明联合体),就是dispatch_object_t能够是结构体中的任意一种类型,大小为这些数据类型中最大的数据类型的大小
    • _os_object_s
      相似于类中的isa指针 image.png
    • dispatch_object_s
      GCD最基础的基类 image.png
    • dispatch_continuation_s
      dispatch_continuation_s结构体主要封装blockfunctiondispatch_async中的block最终都会封装成这个数据类型 image.png
    • dispatch_group_s
      image.png
    • dispatch_queue_s
      image.png
  • 建立队列源码分析

    • dispatch_queue_create源码
      第一步下一个符号断点 image.png 发现源码在libdispatch.dylib库中,而后下载libdispatch.dylib源码搜索dispatch_queue_create能够找到image.png其实冲堆栈信息中也能够知道接下来调用了_dispatch_lane_create_with_target函数来实现建立
    • _dispatch_lane_create_with_target源码
      image.png 大体步骤以下:
      1. 规范化参数
      2. 设置目标队列
        调用_dispatch_get_root_queue方法获取目标队列
        若是是串行队列,则使用_dispatch_get_root_queue(0, true)函数获取目标队列,获取到的目标队列是_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_OVERCOMMIT_PRIORITY]
        若是是并发队列,则使用_dispatch_get_root_queue(0, false)函数获取目标队列,获取到的目标队列是_dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_PRIORITY]
      3. 开辟空间建立队列
        _dispatch_object_alloc开辟空间建立队列
      4. 初始化队列
        _dispatch_queue_init初始化队列,这里根据第一步规范后的参数判断设置最大并发数,DISPATCH_QUEUE_WIDTH_MAX从这里也能够知道最大的并发数是4094
        设置队列标识符等
      5. 赋值目标队列
        将上述设置的目标队列赋值
      具体流程图以下:顺序时间轴.jpg
    • 队列和线程之间的关系图
    image.png
  • 函数源码分析

    • 异步函数分析
      • _dispatch_continuation_init任务包装函数
        主要是copy任务,赋值func、ctxt等参数 image.png 经过_dispatch_continuation_init_f函数赋值func、ctxt等参数 image.png
      • _dispatch_continuation_async并发处理函数
        image.png 全局搜索dx_push发现是个宏#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z),发现仍是调用了dq_push方法继续全局搜索dq_pushiShot2021-04-08 09.15.36.png发现dq_push方法有不少个不过每一个上面有类型不难猜想异步函数应该对应的是queue_concurrent下的方法对应的方法是_dispatch_lane_concurrent_push这里能够经过下符号断点来验证首先如今异步函数钱下个断点image.png而后再往下走发现断在了_dispatch_lane_concurrent_push的方法堆栈上因此也验证了上述猜测image.png,继续往下跟,全局搜索_dispatch_lane_concurrent_pushimage.png任务执行即出队因此通常并行队列中的异步任务的话都是空的因此若是不添加栅栏函数则通常会执行_dispatch_continuation_redirect_push方法这里也能够经过添加符号断点来验证image.pngimage.pngimage.pngimage.png发现的确_dispatch_lane_concurrent_push方法执行完成以后直接进入到_dispatch_continuation_redirect_push方法而后就执行了任务继续全局搜索_dispatch_continuation_redirect_push方法image.png此时的dq_push对应的就不是_dispatch_lane_concurrent_push方法了由于对应的队列类型变成了跟队列,因此此时应该对应的是_dispatch_root_queue_push方法一样的也能够根据上述的步骤下一个符号断点来验证,继续全局搜索_dispatch_root_queue_push方法image.png下个_dispatch_root_queue_push_override符号断点在下一个_dispatch_root_queue_push_inline符号断点发现会走到_dispatch_root_queue_push_override方法中而后image.png里面又继续调用了_dispatch_root_queue_push_inline方法image.png继续查找_dispatch_root_queue_poke方法image.png全局搜索_dispatch_root_queue_poke_slow方法iShot2021-04-08 11.10.05.png到这里异步函数建立线程的流程就已经完成以下图未命名文件(29).jpg可是此时仅仅是建立了线程,可是任务尚未执行此时再看上方的_dispatch_root_queues_init方法点进去发现发现是dispatch_once_f方法调用image.png传入的func_dispatch_root_queues_init_onceiShot2021-04-08 14.40.24.png,继续进到_dispatch_worker_thread2(线程的执行是系统底层去出发的,这里只须要绑定线程,系统会去触发线程)方法查看源码image.png_dispatch_root_queue_drain方法源码image.pngimage.pngimage.png最终发如今这里调用了任务方法,分析以前彻底能够打断点而后打印堆栈调用状况而后再去源码中找对应代码,堆栈调用状况以下:image.png发现堆栈的调用状况就和上述分析的流程一致,
        总流程图以下:未命名文件(30).jpg
    • 同步函数分析
      首先看dispatch_sync源码image.png继续看_dispatch_sync_f源码image.pngimage.png发现同步函数的地方就是经过栅栏函数来实现的(栅栏函数源码以下)
  • 栅栏函数源码分析

    • 同步栅栏函数必须本身的block任务执行完成,下面的任务block才会执行,这就表示同步阻塞的是当前的线程
    • 异步栅栏函数不须要等本身的任务block执行,下面的代码会接着执行,这就表示异步阻塞的是队列(queue)。
    注意:栅栏函数只能用在并发自定义队列中,全局并发队列,若是堵塞了其余系统任务,会形成其余任务,串行队列使用栅栏函数等于没用
    dispatch_barrier_sync源码以下:image.pngimage.pngimage.pngimage.png 同步栅栏函数源码流程:未命名文件(31).jpg
  • 死锁源码分析

    上述栅栏函数分析得知死锁在_dispatch_sync_f_slow方法中处理的image.png首先将当前任务添加到队列中而后执行__DISPATCH_WAIT_FOR_QUEUE__方法image.png再看_dq_state_drain_locked_by方法源码image.pngimage.png判断dq是否为正在等待的队列,而后给出一个状态state,而后将dq的状态和当前任务依赖的队列进行匹配,匹配成功则抛出异常
  • 单列源码分析

    全局搜索dispatch_once查看源码image.png同时发现_dispatch_Block_invoke就是一个宏#define _dispatch_Block_invoke(bb) \ ((dispatch_function_t)((struct Block_layout *)bb)->invoke)就是invoke方法image.pngimage.pngimage.pngimage.pngimage.png 单例底层实现流程图未命名文件(31).jpg
  • 信号量

    控制最大并发量(通常使用很少)核心函数以下:
    1. dispatch_semaphore_create 建立信号量,指定最大并发数
    2. dispatch_semaphore_signal 发送信号
    3. dispatch_semaphore_wait 等待信号
    代码示例以下:
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_semaphore_t sem = dispatch_semaphore_create(2);
        //任务1
        dispatch_async(queue, ^{
            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            NSLog(@"执行任务1");
            sleep(1);
            NSLog(@"任务1完成");
            dispatch_semaphore_signal(sem);
        });
        //任务2
        dispatch_async(queue, ^{
            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            NSLog(@"执行任务2");
            sleep(1);
            NSLog(@"任务2完成");
            dispatch_semaphore_signal(sem);
        });
        //任务3
        dispatch_async(queue, ^{
            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            NSLog(@"执行任务3");
            sleep(1);
            NSLog(@"任务3完成");
            dispatch_semaphore_signal(sem);
        });
    }
    复制代码
    打印结果以下:image.png能够发现由于控制最大的并发量是2因此能同时执行的任务有两个,此时任务一和任务2是同时执行的,任务三是等到前两个任务执行完成以后再执行的
    源码分析:
    1. dispatch_semaphore_create
      主要是初始化工做,设置最大并发量(最大并发量要大于0),封装成dispatch_semaphore_t结构体 image.png
    2. dispatch_semaphore_wait
      主要是对并发量进行--操做,若是当前的并发量已经小于0了说明当前已经达到了最大并发量,因此任务进行无限等待状态(阻塞线程)源码以下image.png os_atomic_dec2o是个宏,由于里面是一个一个宏嵌套,因此这里一个一个剖析后以下;
      os_atomic_dec2o(dsema, dsema_value, acquire);
      os_atomic_sub2o(dsema, dsema_value, 1, acquire)
      os_atomic_sub(&(dsema)->dsema_value, (1), acquire)
      _os_atomic_c11_op(&(dsema)->dsema_value, (1), acquire, sub, -)
      atomic_fetch_sub_explicit(&(dsema)->dsema_value, 1)
      等价于&(dsema)->dsema_value = &(dsema)->dsema_value -1
      复制代码
    3. dispatch_semaphore_signal
      dispatch_semaphore_wait相反,主要是对并发量进行加加操做 image.png
  • 调度组

    调度组主要的做用就是监听某些任务执行状态,好比如今须要刷新UI可是须要两个请求完成以后再刷新UI,此时就可使用调度组,能够等待两个异步任务完成以后收到完成通知后再刷新UI 核心函数:
    • dispatch_group_create 建立组
    • dispatch_group_async 进组任务
    • dispatch_group_notify 等待任务执行完成通知
    • dispatch_group_wait 暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行
    • dispatch_group_enter标志着一个任务追加到 group,执行一次,至关于group 中未执行完毕任务数+1
    • dispatch_group_leave标志着一个任务离开了 group,执行一次,至关于 group 中未执行完毕任务数-1。
    注意:dispatch_group_leavedispatch_group_enter须要成对出现
    使用示例:
    1. 正常状况示例
      复制代码
      打印状况: image.png
    2. 使用dispatch_group_wait
      dispatch_group_t group = dispatch_group_create();
       dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
       dispatch_group_enter(group);
       dispatch_async(queue, ^{
           sleep(1);
           NSLog(@"任务1执行完成");
           dispatch_group_leave(group);
       });
       dispatch_group_enter(group);
       dispatch_async(queue, ^{
           NSLog(@"任务2执行完成");
           dispatch_group_leave(group);
       });
       dispatch_async(dispatch_get_global_queue(0, 0), ^{
           sleep(3);
           NSLog(@"任务3执行完成");
       });
       dispatch_group_notify(group, dispatch_get_main_queue(), ^{
           NSLog(@"任务一、2执行完成");
       });
      
       NSLog(@"任务6执行完成(主线程)");
      复制代码
      打印结果: image.png
    3. 特殊状况1(将通知放在最前面)
      dispatch_group_t group = dispatch_group_create();
       dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
       dispatch_group_notify(group, dispatch_get_main_queue(), ^{
           NSLog(@"任务一、2执行完成");
       });
       dispatch_group_enter(group);
       dispatch_async(queue, ^{
           sleep(1);
           NSLog(@"任务1执行完成");
           dispatch_group_leave(group);
       });
       dispatch_group_enter(group);
       dispatch_async(queue, ^{
           NSLog(@"任务2执行完成");
           dispatch_group_leave(group);
       });
       dispatch_async(dispatch_get_global_queue(0, 0), ^{
           sleep(3);
           NSLog(@"任务3执行完成");
       });    
      
       NSLog(@"任务6执行完成(主线程)");
      复制代码
      打印结果: image.png 说明通知放在最前面只要执行dispatch_group_leave就会执行通知
    4. 特殊状况2(多一个)dispatch_group_enter
      dispatch_group_t group = dispatch_group_create();
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
      
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            sleep(1);
            NSLog(@"任务1执行完成");
            dispatch_group_leave(group);
        });
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"任务2执行完成");
            dispatch_group_leave(group);
        });
        dispatch_group_enter(group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(3);
            NSLog(@"任务3执行完成");
        });
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"任务一、2执行完成");
        });
        NSLog(@"任务6执行完成(主线程)");
      复制代码
      打印状况: image.png 发现dispatch_group_notify一直都不会执行
    5. 特殊状况2(多一个)dispatch_group_leave image.png 直接会崩溃,因此说dispatch_group_leavedispatch_group_enter必定要成对出现
    源码探索:
    • dispatch_group_create
      image.png image.png
    • dispatch_group_enter
      image.png image.png image.png
    • dispatch_group_leave
      image.png image.png
    • dispatch_group_async
      至关于帮开发者省去了dispatch_group_enterdispatch_group_leave两个方法的调用 image.png image.png image.png 发现底层在建立以前调用了dispatch_group_enter方法 image.png 而后再任务执行完毕以后又调用了dispatch_group_leave
    • dispatch_group_notify
      image.png image.png
  • Dispatch Source

    在任⼀线程上调⽤它的的⼀个函数dispatch_source_merge_data后,会执⾏ Dispatch Source 事先定义好的句柄(能够把句柄简单理解为⼀个 block )这个过程叫Custom event,⽤户事件。是dispatch source⽀持处理的⼀种事件
    句柄是⼀种指向指针的指针 它指向的就是⼀个类或者结构,它和系统有很密切的关系 HINSTANCE(实例句柄),HBITMAP(位图句柄),HDC(设备表述句柄),HICON (图标句柄)等。这当中还有⼀个通⽤的句柄,就是HANDLE
    核心方法:
    1. dispatch_source_create建立源
    2. dispatch_source_set_event_handler设置源事件回调
    3. dispatch_source_merge_data源事件设置数据
    4. dispatch_source_get_data获取源事件数据
    5. dispatch_resume继续
    6. dispatch_suspend挂起
    使用示例:
    - (void)viewDidLoad {
         [super viewDidLoad];
    
    
         self.totalComplete = 0;
    
         self.queue = dispatch_queue_create("td.com", NULL);
    
         /* 
         DISPATCH_SOURCE_TYPE_DATA_ADD        自定义的事件,变量增长
         DISPATCH_SOURCE_TYPE_DATA_OR         自定义的事件,变量OR
         DISPATCH_SOURCE_TYPE_DATA_REPLACE    自定义的事件,变量Replace
         DISPATCH_SOURCE_TYPE_MACH_SEND       MACH端口发送    
         DISPATCH_SOURCE_TYPE_MACH_RECV       MACH端口接收 
         DISPATCH_SOURCE_TYPE_MEMORYPRESSURE  内存报警
         DISPATCH_SOURCE_TYPE_PROC            进程监听,如进程的退出、建立一个或更多的子线程、进程收到UNIX信号
         DISPATCH_SOURCE_TYPE_READ            IO操做,如对文件的操做、socket操做的读响应
         DISPATCH_SOURCE_TYPE_SIGNAL          接收到UNIX信号时响应
         DISPATCH_SOURCE_TYPE_TIMER           定时器
         DISPATCH_SOURCE_TYPE_VNODE           文件状态监听,文件被删除、移动、重命名
         DISPATCH_SOURCE_TYPE_WRITE           IO操做,如对文件的操做、socket操做的写响应
         DISPATCH_MACH_SEND_DEAD
         */
         self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    
         dispatch_source_set_event_handler(self.source, ^{
    
             NSLog(@"%@",[NSThread currentThread]);
    
             NSUInteger value = dispatch_source_get_data(self.source);
             self.totalComplete += value;
             NSLog(@"进度: %.2f",self.totalComplete/100.0);
             self.progressView.progress = self.totalComplete/100.0;
         });
    
         self.isRunning = YES;
         dispatch_resume(self.source);
     }
     - (IBAction)didClickStartOrPauseAction:(id)sender {
    
         if (self.isRunning) {
             dispatch_suspend(self.source);
             dispatch_suspend(self.queue);
             NSLog(@"已经暂停");
             self.isRunning = NO;
             [sender setTitle:@"暂停中.." forState:UIControlStateNormal];
         }else{
             dispatch_resume(self.source);
             dispatch_resume(self.queue);
             NSLog(@"已经执行了");
             self.isRunning = YES;
             [sender setTitle:@"暂停中.." forState:UIControlStateNormal];
         }
     }
    
     - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
         NSLog(@"开始了");
    
         for (int i= 0; i<100; i++) {
             dispatch_async(self.queue, ^{
                 if (!self.isRunning) {
                     NSLog(@"已经暂停");
                     return;
                 }
                 sleep(1);
                 dispatch_source_merge_data(self.source, 1);
             });
         }
    
     }
    复制代码
相关文章
相关标签/搜索