经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
iOS底层原理(七)多线程(上)
来源:cnblogs  作者:FunkyRay  时间:2021/4/12 9:49:52  对本文有异议

基本概念

进程和线程

  • 进程:进程是指在系统中正在运行的一个应用程序
  • 线程:1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)
  • 一个进程(程序)的所有任务都在线程中执行
  • 1个线程中任务的执行是串行的

进程和线程的比较

  • 线程是CPU调用(执行任务)的最小单位
  • 进程是CPU分配资源和调度的单位
  • 一个程序可以对应多个进程,一个进程中可以有多个线程,但至少要有一个线程
  • 同一个进程内的线程共享进程的资源

多线程

  • 1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务
  • 同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
  • 多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
  • 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
优点
  • 能适当提高程序的执行效率
  • 能适当提高资源利用率(CPU、内存利用率)
缺点
  • 创建线程是有开销的
  • 如果开启大量的线程,会降低程序的性能
  • 线程越多,CPU在调度线程上的开销就越大
  • 程序设计更加复杂:比如线程之间的通信、多线程的数据共享

主线程

  • 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
  • 显示\刷新UI界面
  • 处理UI事件(比如点击事件、滚动事件、拖拽事件等)
  • 别将比较耗时的操作放到主线程中

iOS中的常见多线程方案

NSThread、GCD、NSOperation底层都是基于pthread来实现的

NSThread

判断以及获取线程的方法

  1. // 1.获得主线程
  2. NSThread *mainThread = [NSThread mainThread];
  3. // 2.获得当前线程
  4. NSThread *currentThread = [NSThread currentThread];
  5. // 3.判断主线程
  6. // 类方法
  7. BOOL isMainThreadA = [NSThread isMainThread];
  8. // 对象方法
  9. BOOL isMainThreadB = [currentThread isMainThread];

创建线程的方法

  1. // 1.手动启动线程
  2. // 可以拿到线程对象进行详细设置
  3. // object:需要传递的参数
  4. NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"ABC"];
  5. // 设置属性
  6. threadA.name = @"线程A";
  7. //设置优先级 取值范围 0.0 ~ 1.0 之间 最高是1.0 默认优先级是0.5
  8. threadA.threadPriority = 1.0;
  9. // 启动线程
  10. [threadA start];
  11. // 2.自动启动线程
  12. // 无法对线程进行更详细的设置
  13. [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分离子线程"];
  14. // 3.开启一条后台线程
  15. [self performSelectorInBackground:@selector(run:) withObject:@"开启后台线程"];

其他常用方法

  1. // 1.阻塞线程
  2. [NSThread sleepForTimeInterval:2.0];
  3. [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
  4. // 2.回到主线程
  5. // waitUntilDone:是否需要等待
  6. [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
  7. [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
  8. // 3.可以设置在哪个线程执行
  9. [self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
  10. // 4.退出线程
  11. //注意:线程死了不能复生
  12. [NSThread exit];

NSOperation

NSOperation的基本使用

  1. // 1.创建操作
  2. // alloc init 方式创建操作
  3. NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
  4. // block 方式创建操作
  5. NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
  6. NSLog(@"1----%@",[NSThread currentThread]);
  7. }];
  8. // 2.追加任务
  9. // 注意:如果一个操作中的任务数量大于1,那么会开子线程并发执行任务
  10. // 注意:不一定是子线程,有可能是主线程
  11. [op2 addExecutionBlock:^{
  12. NSLog(@"2---%@",[NSThread currentThread]);
  13. }];
  14. // 3.启动
  15. [op1 start];
  16. [op2 start];
  17. // 4.操作监听
  18. // 执行操作完毕后会执行该回调
  19. op2.completionBlock = ^{
  20. NSLog(@"%@",[NSThread currentThread]);
  21. };
  22. // 5.设置依赖
  23. // 注意点:不能循环依赖
  24. // 可以跨队列依赖
  25. [op1 addDependency:op2];

队列的基本使用

第一种创建方式

  1. // 1.创建操作,封装任务
  2. NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
  3. // 2.创建队列
  4. // 非主队列: (同时具备并发和串行的功能)
  5. // 默认情况下,非主队列是并发队列
  6. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  7. // 3.添加操作到队列中
  8. [queue addOperation:op1]; //内部已经调用了[op1 start]

第二种创建方式

  1. // 1.创建操作
  2. NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
  3. NSLog(@"1----%@",[NSThread currentThread]);
  4. }];
  5. NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
  6. NSLog(@"2----%@",[NSThread currentThread]);
  7. }];
  8. // 追加任务
  9. [op2 addExecutionBlock:^{
  10. NSLog(@"3----%@",[NSThread currentThread]);
  11. }];
  12. // 2.创建队列
  13. NSOperationQueue *queue = [[NSOperationQueue alloc]init];
  14. // 3.添加操作到队列
  15. [queue addOperation:op1];
  16. [queue addOperation:op2];

第三种创建方式

  1. // 1.创建队列
  2. NSOperationQueue *queue = [[NSOperationQueue alloc]init];
  3. // 2.队列直接添加一个操作(省略创建操作)
  4. [queue addOperationWithBlock:^{
  5. NSLog(@"1----%@",[NSThread currentThread]);
  6. }];

第四种创建方式

  1. // 1.创建队列
  2. // LLOperation继承自NSOperation
  3. LLOperation *op1 = [[LLOperation alloc]init];
  4. // 2.LLOperation内部重写 main 方法
  5. - (void)main {
  6. NSLog(@"main---%@",[NSThread currentThread]);
  7. }
  8. // 3.创建队列
  9. NSOperationQueue *queue = [[NSOperationQueue alloc]init];
  10. // 4.添加操作到队列
  11. [queue addOperation:op1];

队列的其他用法

  1. NSOperationQueue *queue = [[NSOperationQueue alloc]init];
  2. // 1.设置最大并发数量
  3. /*
  4. 同一时间最多有多少个任务可以执行
  5. 串行执行任务!=只开一条线程 (线程同步)
  6. maxConcurrentOperationCount >1 那么就是并发队列
  7. maxConcurrentOperationCount == 1 那就是串行队列
  8. maxConcurrentOperationCount == 0 不会执行任务
  9. maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制
  10. */
  11. queue.maxConcurrentOperationCount = 5;
  12. // 2.暂停(可以恢复)
  13. // YES代表暂停队列,NO代表恢复队列
  14. /*
  15. 队列中的任务也是有状态的:已经执行完毕的 | 正在执行 | 排队等待状态
  16. 不能暂停当前正在处于执行状态的任务
  17. */
  18. [queue setSuspended:YES];
  19. // 3.取消(不可以恢复)
  20. // 该方法内部调用了所有操作的cancel方法
  21. [queue cancelAllOperations];
  22. // 4.创建主队列
  23. // 会在主线程执行操作,不开线程
  24. NSOperationQueue *queue = [NSOperationQueue mainQueue];

总结:

  • NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
  • 默认情况下,NSInvocationOperation调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作;
    只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
  • 只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
  • 操作之间不能相互依赖,会造成循环依赖
  • 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应

GCD

GCD的队列

GCD的队列可以分为2大类型
  • 并发队列(Concurrent Dispatch Queue)
    • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    • 并发功能只有在异步dispatch_async函数下才有效
  • 串行队列(Serial Dispatch Queue)
    • 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
  1. // 1.创建队列
  2. /*
  3. 第一个参数:C语言的字符串,标签
  4. 第二个参数:队列的类型
  5. DISPATCH_QUEUE_CONCURRENT:并发
  6. DISPATCH_QUEUE_SERIAL:串行
  7. */
  8. // 并发队列
  9. dispatch_queue_t queue = dispatch_queue_create("com.haha", DISPATCH_QUEUE_CONCURRENT);
  10. // 串行队列
  11. dispatch_queue_t queue = dispatch_queue_create("com.haha", DISPATCH_QUEUE_SERIAL);
  12. // 2.获得全局并发队列
  13. // 第一个参数:可以设置优先级
  14. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  15. // 3.获得主队列
  16. dispatch_queue_t queue = dispatch_get_main_queue();
  17. // 4.异步函数
  18. dispatch_async(queue, ^{
  19. NSLog(@"download1----%@",[NSThread currentThread]);
  20. });
  21. // 5.同步函数
  22. dispatch_sync(queue, ^{
  23. NSLog(@"download2----%@",[NSThread currentThread]);
  24. });
同步、异步、并发、串行的注意点:
  • 同步和异步主要影响:能不能开启新的线程 - 同步:在当前线程中执行任务,不具备开启新线程的能力 - 异步:在新的线程中执行任务,具备开启新线程的能力- 并发和串行主要影响:任务的执行方式 - 并发:多个任务并发(同时)执行 - 串行:一个任务执行完毕后,再执行下一个任务

创建一个同步串行队列

  1. // 不论是哪种队列,都不会开启新线程
  2. dispatch_queue_t queue = dispatch_queue_create("com.haha", DISPATCH_QUEUE_SERIAL);
  3. // dispatch_queue_t queue = dispatch_queue_create("com.haha", DISPATCH_QUEUE_CONCURRENT);
  4. // dispatch_queue_t queue = dispatch_get_main_queue();
  5. dispatch_sync(queue, ^{
  6. NSLog(@"%@", [NSThread currentThread]);
  7. });
  8. dispatch_sync(queue, ^{
  9. NSLog(@"%@", [NSThread currentThread]);
  10. });
  11. // 打印输出:
  12. // <NSThread: 0x6000020198c0>{number = 1, name = main}
  13. // <NSThread: 0x6000020191c0>{number = 1, name = main}

创建一个异步并发队列

  1. // 并发队列
  2. dispatch_queue_t queue = dispatch_queue_create("com.haha", DISPATCH_QUEUE_CONCURRENT);
  3. dispatch_async(queue, ^{
  4. NSLog(@"%@", [NSThread currentThread]);
  5. });
  6. dispatch_async(queue, ^{
  7. NSLog(@"%@", [NSThread currentThread]);
  8. });
  9. // 打印输出:
  10. // <NSThread: 0x6000020198c0>{number = 4, name = (null)}
  11. // <NSThread: 0x6000020191c0>{number = 5, name = (null)}

创建一个异步串行队列

  1. // 串行队列
  2. dispatch_queue_t queue = dispatch_queue_create("com.haha", DISPATCH_QUEUE_SERIAL);
  3. dispatch_async(queue, ^{
  4. NSLog(@"%@", [NSThread currentThread]);
  5. });
  6. dispatch_async(queue, ^{
  7. NSLog(@"%@", [NSThread currentThread]);
  8. });
  9. // 打印输出:
  10. // <NSThread: 0x6000020198c0>{number = 5, name = (null)}
  11. // <NSThread: 0x6000020191c0>{number = 5, name = (null)}

在主队列中,不论是同步还是异步都不会开启子线程

  1. dispatch_queue_t queue = dispatch_get_main_queue();
  2. dispatch_async(queue, ^{
  3. NSLog(@"%@", [NSThread currentThread]);
  4. });
  5. // 打印输出:
  6. // <NSThread: 0x6000020198c0>{number = 1, name = main}

但是使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)

综上所述可以用一张图来概述

dispatch_get_global_queuedispatch_queue_create的区别

我们在代码里分别创建两种队列,然后打印发现,全局队列的地址都是同一个,而dispatch_queue_create的对象都不相同

  1. dispatch_queue_t queue1 = dispatch_get_global_queue(0, 0);
  2. dispatch_queue_t queue2 = dispatch_get_global_queue(0, 0);
  3. dispatch_queue_t queue3 = dispatch_queue_create("queu3", DISPATCH_QUEUE_CONCURRENT);
  4. dispatch_queue_t queue4 = dispatch_queue_create("queu4", DISPATCH_QUEUE_CONCURRENT);
  5. dispatch_queue_t queue5 = dispatch_queue_create("queu5", DISPATCH_QUEUE_CONCURRENT);
  6. NSLog(@"%p %p %p %p %p", queue1, queue2, queue3, queue4, queue5);
  7. // 分别输出:0x10c5d8080 0x10c5d8080 0x6000037c3180 0x6000037c1580 0x6000037c3200

GCD的队列组

第一种创建方式

  1. // 1.创建队列
  2. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  3. // 2.创建队列组
  4. dispatch_group_t group = dispatch_group_create();
  5. // 3.把任务添加到队列中
  6. dispatch_group_async(group, queue, ^{
  7. NSLog(@"1----%@",[NSThread currentThread]);
  8. });
  9. dispatch_group_async(group, queue, ^{
  10. NSLog(@"2----%@",[NSThread currentThread]);
  11. });
  12. // 4.拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
  13. dispatch_group_notify(group, queue, ^{
  14. NSLog(@"-------dispatch_group_notify-------");
  15. });

第二种创建方式

  1. // 1.创建队列
  2. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  3. // 2.创建队列组
  4. dispatch_group_t group = dispatch_group_create();
  5. // 3.在该方法后面的异步任务会被纳入到队列组的监听范围,进入群组
  6. // dispatch_group_enter|dispatch_group_leave 必须要配对使用
  7. dispatch_group_enter(group);
  8. dispatch_async(queue, ^{
  9. NSLog(@"1----%@",[NSThread currentThread]);
  10. //离开群组
  11. dispatch_group_leave(group);
  12. });
  13. dispatch_group_enter(group);
  14. dispatch_async(queue, ^{
  15. NSLog(@"2----%@",[NSThread currentThread]);
  16. //离开群组
  17. dispatch_group_leave(group);
  18. });
  19. // 拦截通知
  20. // 内部本身是异步的
  21. dispatch_group_notify(group, queue, ^{
  22. NSLog(@"-------dispatch_group_notify-------");
  23. });

第三种方式

  1. // 1.创建队列
  2. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  3. // 2.创建队列组
  4. dispatch_group_t group = dispatch_group_create();
  5. // 3.把任务添加到队列中
  6. dispatch_group_async(group, queue, ^{
  7. NSLog(@"1----%@",[NSThread currentThread]);
  8. });
  9. dispatch_group_async(group, queue, ^{
  10. NSLog(@"2----%@",[NSThread currentThread]);
  11. });
  12. // 4.会阻塞线程
  13. // 直到队列组中所有的任务都执行完毕之后才能执行
  14. dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
  15. NSLog(@"----end----");

其他常用方法

  1. // 1.延迟执行的几种方法
  2. // 1.1
  3. [self performSelector:@selector(task) withObject:nil afterDelay:2.0];
  4. // 1.2
  5. // repeats:是否重复调用
  6. [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
  7. // 1.3
  8. // 可以设置队列控制在哪个线程执行延迟
  9. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  10. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
  11. NSLog(@"GCD----%@",[NSThread currentThread]);
  12. });
  13. // 2.一次性代码
  14. // 整个程序运行过程中只会执行一次
  15. // onceToken用来记录该部分的代码是否被执行过
  16. static dispatch_once_t onceToken;
  17. dispatch_once(&onceToken, ^{
  18. NSLog(@"---once----");
  19. });
  20. // 3.快速遍历
  21. // 开多个线程进行遍历
  22. /*
  23. 第一个参数:遍历的次数
  24. 第二个参数:队列(并发队列)
  25. 第三个参数:index 索引
  26. */
  27. dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
  28. NSLog(@"%zd---%@",index,[NSThread currentThread]);
  29. });
  30. // 4.栅栏函数
  31. // 栅栏函数不能使用全局并发队列
  32. // 栅栏函数之后的线程都会延后执行
  33. dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
  34. dispatch_async(queue, ^{
  35. NSLog(@"%@", [NSThread currentThread]);
  36. });
  37. dispatch_barrier_async(queue, ^{
  38. NSLog(@"+++++++++++++++++++++++++++++");
  39. });
  40. dispatch_async(queue, ^{
  41. NSLog(@"%@", [NSThread currentThread]);
  42. });

分析底层实现

源码下载

我们可以通过GCD的源码libdispatch.dylib来分析内部实现

libdispatch.dylib的下载地址:https://opensource.apple.com/release/macos-1015.html

然后找到libdispatch-1173.0.3进行下载

源码分析

dispatch_queue_create的底层实现

我们在queue.c文件中搜索dispatch_queue_create,可以找到对应实现

  1. dispatch_queue_t
  2. dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
  3. {
  4. return _dispatch_lane_create_with_target(label, attr,
  5. DISPATCH_TARGET_QUEUE_DEFAULT, true);
  6. }

进入_dispatch_lane_create_with_target

  1. DISPATCH_NOINLINE
  2. static dispatch_queue_t
  3. _dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
  4. dispatch_queue_t tq, bool legacy)
  5. {
  6. // dqai 创建
  7. dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
  8. //
  9. // Step 1: Normalize arguments (qos, overcommit, tq)
  10. // 第一步:规范化参数,例如qos, overcommit, tq
  11. dispatch_qos_t qos = dqai.dqai_qos;
  12. #if !HAVE_PTHREAD_WORKQUEUE_QOS
  13. if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
  14. dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
  15. }
  16. if (qos == DISPATCH_QOS_MAINTENANCE) {
  17. dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
  18. }
  19. #endif // !HAVE_PTHREAD_WORKQUEUE_QOS
  20. _dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
  21. if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
  22. if (tq->do_targetq) {
  23. DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
  24. "a non-global target queue");
  25. }
  26. }
  27. if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
  28. // Handle discrepancies between attr and target queue, attributes win
  29. if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
  30. if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
  31. overcommit = _dispatch_queue_attr_overcommit_enabled;
  32. } else {
  33. overcommit = _dispatch_queue_attr_overcommit_disabled;
  34. }
  35. }
  36. if (qos == DISPATCH_QOS_UNSPECIFIED) {
  37. qos = _dispatch_priority_qos(tq->dq_priority);
  38. }
  39. tq = NULL;
  40. } else if (tq && !tq->do_targetq) {
  41. // target is a pthread or runloop root queue, setting QoS or overcommit
  42. // is disallowed
  43. if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
  44. DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
  45. "and use this kind of target queue");
  46. }
  47. } else {
  48. if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
  49. // Serial queues default to overcommit!
  50. overcommit = dqai.dqai_concurrent ?
  51. _dispatch_queue_attr_overcommit_disabled :
  52. _dispatch_queue_attr_overcommit_enabled;
  53. }
  54. }
  55. if (!tq) {
  56. tq = _dispatch_get_root_queue(
  57. qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
  58. overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
  59. if (unlikely(!tq)) {
  60. DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
  61. }
  62. }
  63. //
  64. // Step 2: Initialize the queue
  65. // 第二步:初始化队列
  66. if (legacy) {
  67. // if any of these attributes is specified, use non legacy classes
  68. if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
  69. legacy = false;
  70. }
  71. }
  72. // 拼接队列名称
  73. const void *vtable;
  74. dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
  75. if (dqai.dqai_concurrent) {
  76. vtable = DISPATCH_VTABLE(queue_concurrent);
  77. } else {
  78. vtable = DISPATCH_VTABLE(queue_serial);
  79. }
  80. switch (dqai.dqai_autorelease_frequency) {
  81. case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
  82. dqf |= DQF_AUTORELEASE_NEVER;
  83. break;
  84. case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
  85. dqf |= DQF_AUTORELEASE_ALWAYS;
  86. break;
  87. }
  88. if (label) {
  89. const char *tmp = _dispatch_strdup_if_mutable(label);
  90. if (tmp != label) {
  91. dqf |= DQF_LABEL_NEEDS_FREE;
  92. label = tmp;
  93. }
  94. }
  95. // 创建队列,并初始化
  96. dispatch_lane_t dq = _dispatch_object_alloc(vtable,
  97. sizeof(struct dispatch_lane_s));
  98. // 根据dqai.dqai_concurrent的值,判断是串行还是并发队列
  99. _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
  100. DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
  101. (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
  102. // 队列label的标识符
  103. dq->dq_label = label;
  104. dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
  105. dqai.dqai_relpri);
  106. if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
  107. dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
  108. }
  109. if (!dqai.dqai_inactive) {
  110. _dispatch_queue_priority_inherit_from_target(dq, tq);
  111. _dispatch_lane_inherit_wlh_from_target(dq, tq);
  112. }
  113. _dispatch_retain(tq);
  114. dq->do_targetq = tq;
  115. _dispatch_object_debug(dq, "%s", __func__);
  116. return _dispatch_trace_queue_create(dq)._dq;
  117. }

_dispatch_trace_queue_create内部会一步步调用_dispatch_introspection_queue_create -> _dispatch_introspection_queue_create_hook -> dispatch_introspection_queue_get_info,最终可以找到是通过_dispatch_introspection_lane_get_info通过模板来创建的队列

  1. DISPATCH_ALWAYS_INLINE
  2. static inline dispatch_introspection_queue_s
  3. _dispatch_introspection_lane_get_info(dispatch_lane_class_t dqu)
  4. {
  5. dispatch_lane_t dq = dqu._dl;
  6. bool global = _dispatch_object_is_global(dq);
  7. uint64_t dq_state = os_atomic_load2o(dq, dq_state, relaxed);
  8. dispatch_introspection_queue_s diq = {
  9. .queue = dq->_as_dq,
  10. .target_queue = dq->do_targetq,
  11. .label = dq->dq_label,
  12. .serialnum = dq->dq_serialnum,
  13. .width = dq->dq_width,
  14. .suspend_count = _dq_state_suspend_cnt(dq_state) + dq->dq_side_suspend_cnt,
  15. .enqueued = _dq_state_is_enqueued(dq_state) && !global,
  16. .barrier = _dq_state_is_in_barrier(dq_state) && !global,
  17. .draining = (dq->dq_items_head == (void*)~0ul) ||
  18. (!dq->dq_items_head && dq->dq_items_tail),
  19. .global = global,
  20. .main = dx_type(dq) == DISPATCH_QUEUE_MAIN_TYPE,
  21. };
  22. return diq;
  23. }

_dispatch_lane_create_with_target详细步骤解析

1.调用_dispatch_queue_attr_to_info传入dqa,创建dispatch_queue_attr_info_t类型的dqai,用于存储队列的相关属性信息

  1. dispatch_queue_attr_info_t
  2. _dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
  3. {
  4. dispatch_queue_attr_info_t dqai = { };
  5. if (!dqa) return dqai;
  6. #if DISPATCH_VARIANT_STATIC
  7. // 默认为serial和null
  8. if (dqa == &_dispatch_queue_attr_concurrent) {
  9. dqai.dqai_concurrent = true;
  10. return dqai;
  11. }
  12. #endif
  13. if (dqa < _dispatch_queue_attrs ||
  14. dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
  15. DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
  16. }
  17. size_t idx = (size_t)(dqa - _dispatch_queue_attrs);
  18. dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
  19. idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;
  20. dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
  21. idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;
  22. dqai.dqai_relpri = -(int)(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
  23. idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;
  24. dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT;
  25. idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;
  26. dqai.dqai_autorelease_frequency =
  27. idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
  28. idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
  29. dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
  30. idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
  31. return dqai;
  32. }

2.通过_dispatch_object_alloc创建队列dq

  1. void *
  2. _dispatch_object_alloc(const void *vtable, size_t size)
  3. {
  4. #if OS_OBJECT_HAVE_OBJC1
  5. const struct dispatch_object_vtable_s *_vtable = vtable;
  6. dispatch_object_t dou;
  7. dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
  8. dou._do->do_vtable = vtable;
  9. return dou._do;
  10. #else
  11. return _os_object_alloc_realized(vtable, size);
  12. #endif
  13. }

内部调用_os_object_alloc_realized,可以看出队列内部也有一个isa指针,所以队列也是对象

  1. inline _os_object_t
  2. _os_object_alloc_realized(const void *cls, size_t size)
  3. {
  4. _os_object_t obj;
  5. dispatch_assert(size >= sizeof(struct _os_object_s));
  6. while (unlikely(!(obj = calloc(1u, size)))) {
  7. _dispatch_temporary_resource_shortage();
  8. }
  9. obj->os_obj_isa = cls;
  10. return obj;
  11. }

3.通过_dispatch_queue_init设置队列的相关属性

  1. static inline dispatch_queue_class_t
  2. _dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf,
  3. uint16_t width, uint64_t initial_state_bits)
  4. {
  5. uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);
  6. dispatch_queue_t dq = dqu._dq;
  7. dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK |
  8. DISPATCH_QUEUE_INACTIVE)) == 0);
  9. if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) {
  10. dq->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume
  11. if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
  12. dq->do_ref_cnt++; // released when DSF_DELETED is set
  13. }
  14. }
  15. dq_state |= initial_state_bits;
  16. dq->do_next = DISPATCH_OBJECT_LISTLESS;
  17. dqf |= DQF_WIDTH(width);
  18. os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);
  19. dq->dq_state = dq_state;
  20. dq->dq_serialnum =
  21. os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);
  22. return dqu;
  23. }

总结:

上述分析可以通过下图来概述

dispatch_async的底层实现

queue.c中找到dispatch_async,其内部的实现如下

  1. void
  2. dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
  3. {
  4. dispatch_continuation_t dc = _dispatch_continuation_alloc();
  5. uintptr_t dc_flags = DC_FLAG_CONSUME;
  6. dispatch_qos_t qos;
  7. // 任务包装函数
  8. qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
  9. // 并发处理函数
  10. _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
  11. }

1.在_dispatch_continuation_init中进行任务的封装

  1. DISPATCH_ALWAYS_INLINE
  2. static inline dispatch_qos_t
  3. _dispatch_continuation_init(dispatch_continuation_t dc,
  4. dispatch_queue_class_t dqu, dispatch_block_t work,
  5. dispatch_block_flags_t flags, uintptr_t dc_flags)
  6. {
  7. // 拷贝任务
  8. void *ctxt = _dispatch_Block_copy(work);
  9. dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
  10. if (unlikely(_dispatch_block_has_private_data(work))) {
  11. dc->dc_flags = dc_flags;
  12. dc->dc_ctxt = ctxt; // 赋值
  13. // will initialize all fields but requires dc_flags & dc_ctxt to be set
  14. return _dispatch_continuation_init_slow(dc, dqu, flags);
  15. }
  16. // 封装任务,异步回调
  17. dispatch_function_t func = _dispatch_Block_invoke(work);
  18. if (dc_flags & DC_FLAG_CONSUME) {
  19. func = _dispatch_call_block_and_release;
  20. }
  21. return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
  22. }

_dispatch_Block_invoke是一个宏,主要就是对任务的封装

  1. #define _dispatch_Block_invoke(bb) ((dispatch_function_t)((struct Block_layout *)bb)->invoke)

2.在_dispatch_continuation_async中并发处理函数

  1. DISPATCH_ALWAYS_INLINE
  2. static inline void
  3. _dispatch_continuation_async(dispatch_queue_class_t dqu,
  4. dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
  5. {
  6. #if DISPATCH_INTROSPECTION
  7. if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
  8. _dispatch_trace_item_push(dqu, dc); // 跟踪日志
  9. }
  10. #else
  11. (void)dc_flags;
  12. #endif
  13. return dx_push(dqu._dq, dc, qos);
  14. }

其中的dx_push是一个宏

  1. #define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

其中的dq_push需要根据队列的类型,执行不同的函数

总结:

上述分析可以通过下图来概述

dispatch_sync的底层实现

queue.c中找到dispatch_async,其内部的实现如下

  1. void
  2. dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
  3. {
  4. uintptr_t dc_flags = DC_FLAG_BLOCK;
  5. if (unlikely(_dispatch_block_has_private_data(work))) {
  6. return _dispatch_sync_block_with_privdata(dq, work, dc_flags);
  7. }
  8. _dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
  9. }

然后调用到_dispatch_sync_f

  1. static void
  2. _dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
  3. uintptr_t dc_flags)
  4. {
  5. _dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
  6. }

然后调用到_dispatch_sync_f_inline,发现其内部是用栅栏函数实现的

  1. static inline void
  2. _dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
  3. dispatch_function_t func, uintptr_t dc_flags)
  4. {
  5. if (likely(dq->dq_width == 1)) { // 表示串行队列
  6. return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags); // 栅栏函数
  7. }
  8. if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {
  9. DISPATCH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");
  10. }
  11. dispatch_lane_t dl = upcast(dq)._dl;
  12. // Global concurrent queues and queues bound to non-dispatch threads
  13. // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
  14. if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {
  15. return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags); // 死锁
  16. }
  17. if (unlikely(dq->do_targetq->do_targetq)) {
  18. return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
  19. }
  20. _dispatch_introspection_sync_begin(dl); // 处理当前信息
  21. _dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
  22. _dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags))); // block执行并释放
  23. }

_dispatch_sync_f_slow中,当前的主队列会被阻塞挂起

  1. static void
  2. _dispatch_sync_f_slow(dispatch_queue_class_t top_dqu, void *ctxt,
  3. dispatch_function_t func, uintptr_t top_dc_flags,
  4. dispatch_queue_class_t dqu, uintptr_t dc_flags)
  5. {
  6. dispatch_queue_t top_dq = top_dqu._dq;
  7. dispatch_queue_t dq = dqu._dq;
  8. if (unlikely(!dq->do_targetq)) {
  9. return _dispatch_sync_function_invoke(dq, ctxt, func);
  10. }
  11. pthread_priority_t pp = _dispatch_get_priority();
  12. struct dispatch_sync_context_s dsc = {
  13. .dc_flags = DC_FLAG_SYNC_WAITER | dc_flags,
  14. .dc_func = _dispatch_async_and_wait_invoke,
  15. .dc_ctxt = &dsc,
  16. .dc_other = top_dq,
  17. .dc_priority = pp | _PTHREAD_PRIORITY_ENFORCE_FLAG,
  18. .dc_voucher = _voucher_get(),
  19. .dsc_func = func,
  20. .dsc_ctxt = ctxt,
  21. .dsc_waiter = _dispatch_tid_self(),
  22. };
  23. _dispatch_trace_item_push(top_dq, &dsc);
  24. __DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);
  25. if (dsc.dsc_func == NULL) {
  26. dispatch_queue_t stop_dq = dsc.dc_other;
  27. return _dispatch_sync_complete_recurse(top_dq, stop_dq, top_dc_flags);
  28. }
  29. _dispatch_introspection_sync_begin(top_dq);
  30. _dispatch_trace_item_pop(top_dq, &dsc);
  31. _dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags
  32. DISPATCH_TRACE_ARG(&dsc));
  33. }

总结:

上述分析可以通过下图来概述

原文链接:http://www.cnblogs.com/funkyRay/p/ios-di-ceng-yuan-li-qi-duo-xian-cheng-shang.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号