-
常见数据结构
-
dispatch_object_tGCD所有类的基类从源码可以看出
dispatch_object_t是一个联合体(而且是一个透明联合体),就是dispatch_object_t可以是结构体中的任意一种类型,大小为这些数据类型中最大的数据类型的大小 -
类似于类中的isa指针_os_object_s -
GCD最基础的基类dispatch_object_s -
dispatch_continuation_sdispatch_continuation_s结构体主要封装block和function,dispatch_async中的block最终都会封装成这个数据类型 -
dispatch_group_s -
dispatch_queue_s
-
-
创建队列源码分析
-
第一步下一个符号断点dispatch_queue_create源码发现源码在
libdispatch.dylib库中,然后下载libdispatch.dylib源码搜索dispatch_queue_create可以找到其实冲堆栈信息中也可以知道接下来调用了
_dispatch_lane_create_with_target函数来实现创建 -
_dispatch_lane_create_with_target源码大致步骤如下:
- 规范化参数
- 设置目标队列
调用_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]。 - 开辟空间创建队列
_dispatch_object_alloc开辟空间创建队列 - 初始化队列
_dispatch_queue_init初始化队列,这里根据第一步规范后的参数判断设置最大并发数,DISPATCH_QUEUE_WIDTH_MAX从这里也可以知道最大的并发数是4094
设置队列标识符等 - 赋值目标队列
将上述设置的目标队列赋值 具体流程图如下:
-
队列和线程之间的关系图
-
-
函数源码分析
-
异步函数分析
-
主要是copy任务,赋值func、ctxt等参数_dispatch_continuation_init任务包装函数通过
_dispatch_continuation_init_f函数赋值func、ctxt等参数 -
_dispatch_continuation_async并发处理函数全局搜索
dx_push发现是个宏#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z),发现还是调用了dq_push方法继续全局搜索dq_push发现
dq_push方法有很多个不过每个上面有类型不难猜测异步函数应该对应的是queue_concurrent下的方法对应的方法是_dispatch_lane_concurrent_push这里可以通过下符号断点来验证首先现在异步函数钱下个断点然后再往下走发现断在了
_dispatch_lane_concurrent_push的方法堆栈上所以也验证了上述猜想,继续往下跟,全局搜索
_dispatch_lane_concurrent_push任务执行即出队所以一般并行队列中的异步任务的话都是空的所以如果不添加栅栏函数则一般会执行
_dispatch_continuation_redirect_push方法这里也可以通过添加符号断点来验证发现的确
_dispatch_lane_concurrent_push方法执行完成之后直接进入到_dispatch_continuation_redirect_push方法然后就执行了任务继续全局搜索_dispatch_continuation_redirect_push方法此时的
dq_push对应的就不是_dispatch_lane_concurrent_push方法了因为对应的队列类型变成了跟队列,所以此时应该对应的是_dispatch_root_queue_push方法同样的也可以根据上述的步骤下一个符号断点来验证,继续全局搜索_dispatch_root_queue_push方法下个
_dispatch_root_queue_push_override符号断点在下一个_dispatch_root_queue_push_inline符号断点发现会走到_dispatch_root_queue_push_override方法中然后里面又继续调用了
_dispatch_root_queue_push_inline方法继续查找
_dispatch_root_queue_poke方法全局搜索
_dispatch_root_queue_poke_slow方法到这里异步函数创建线程的流程就已经完成如下图
但是此时仅仅是创建了线程,但是任务还没有执行此时再看上方的
_dispatch_root_queues_init方法点进去发现发现是dispatch_once_f方法调用传入的
func是_dispatch_root_queues_init_once,继续进到
_dispatch_worker_thread2(线程的执行是系统底层去出发的,这里只需要绑定线程,系统会去触发线程)方法查看源码_dispatch_root_queue_drain方法源码最终发现在这里调用了任务方法,分析之前完全可以打断点然后打印堆栈调用情况然后再去源码中找对应代码,堆栈调用情况如下:
发现堆栈的调用情况就和上述分析的流程一致,
总流程图如下:
-
-
同步函数分析
首先看dispatch_sync源码继续看
_dispatch_sync_f源码发现同步函数的地方就是通过栅栏函数来实现的(栅栏函数源码如下)
-
-
栅栏函数源码分析
- 同步栅栏函数必须自己的block任务执行完成,下面的任务block才会执行,这就表示同步阻塞的是当前的线程
- 异步栅栏函数不需要等自己的任务block执行,下面的代码会接着执行,这就表示异步阻塞的是队列(queue)。
注意:栅栏函数只能用在并发自定义队列中,全局并发队列,如果堵塞了其他系统任务,会造成其他任务,串行队列使用栅栏函数等于没用
dispatch_barrier_sync源码如下:同步栅栏函数源码流程:
-
死锁源码分析
上述栅栏函数分析得知死锁在_dispatch_sync_f_slow方法中处理的首先将当前任务添加到队列中然后执行
__DISPATCH_WAIT_FOR_QUEUE__方法再看
_dq_state_drain_locked_by方法源码判断dq是否为正在等待的队列,然后给出一个状态state,然后将dq的状态和当前任务依赖的队列进行匹配,匹配成功则抛出异常
-
单列源码分析
全局搜索dispatch_once查看源码同时发现
_dispatch_Block_invoke就是一个宏#define _dispatch_Block_invoke(bb) \ ((dispatch_function_t)((struct Block_layout *)bb)->invoke)就是invoke方法单例底层实现流程图
-
信号量
控制最大并发量(一般使用不多)核心函数如下:dispatch_semaphore_create创建信号量,指定最大并发数dispatch_semaphore_signal发送信号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); }); }可以发现因为控制最大的并发量是2所以能同时执行的任务有两个,此时任务一和任务2是同时执行的,任务三是等到前两个任务执行完成之后再执行的
源码分析:-
主要是初始化工作,设置最大并发量(最大并发量要大于0),封装成dispatch_semaphore_createdispatch_semaphore_t结构体 -
主要是对并发量进行--操作,如果当前的并发量已经小于0了说明当前已经达到了最大并发量,所以任务进行无限等待状态(阻塞线程)源码如下dispatch_semaphore_waitos_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 -
和dispatch_semaphore_signaldispatch_semaphore_wait相反,主要是对并发量进行加加操作
-
调度组
调度组主要的作用就是监听某些任务执行状态,比如现在需要刷新UI但是需要两个请求完成之后再刷新UI,此时就可以使用调度组,可以等待两个异步任务完成之后收到完成通知后再刷新UI 核心函数:dispatch_group_create创建组dispatch_group_async进组任务dispatch_group_notify等待任务执行完成通知dispatch_group_wait暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行dispatch_group_enter标志着一个任务追加到 group,执行一次,相当于group 中未执行完毕任务数+1dispatch_group_leave标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。
注意:dispatch_group_leave和dispatch_group_enter需要成对出现
使用示例:
- 正常情况示例
打印情况: - 使用
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(@"任务1、2执行完成"); }); NSLog(@"任务6执行完成(主线程)"); - 特殊情况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(@"任务1、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执行完成(主线程)");说明通知放在最前面只要执行
dispatch_group_leave就会执行通知 - 特殊情况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(@"任务1、2执行完成"); }); NSLog(@"任务6执行完成(主线程)");发现
dispatch_group_notify一直都不会执行 - 特殊情况2(多一个)
dispatch_group_leave直接会崩溃,所以说
dispatch_group_leave和dispatch_group_enter一定要成对出现 源码探索:
-
dispatch_group_create -
dispatch_group_enter -
dispatch_group_leave -
相当于帮开发者省去了dispatch_group_asyncdispatch_group_enter和dispatch_group_leave两个方法的调用发现底层在创建之前调用了
dispatch_group_enter方法然后再任务执行完毕之后又调用了
dispatch_group_leave -
dispatch_group_notify
-
Dispatch Source
在任⼀线程上调⽤它的的⼀个函数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挂起 使用示例:
- (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); }); } }