GCD

GCD 是 Cocoa 中提供的系统级线程管理技术;它将多线程的管理抽象为队列 Queue。只需要将需要执行的任务添加到 Queue 中,GCD 就能调度或生成线程执行任务;同时对线程的管理是作为系统的一部分来实现的,可以实现线程的统一管理,效率也是最好的

Queue

Dispatch Queue 是需要执行处理的等待队列;当需要执行的任务被添加到 Queue 中之后会按照先进先出的顺序被取出执行

Dispatch Queue 是一个链表实现的队列,其中链节点是一个 dispatch_continuation_t 结构体,里面包含了所执行的任务函数以及一些额外信息

Serial Queue 和 Concurrent Queue

Dispatch Queue 分为线性执行的 Serial Dispatch Queue 和并发执行的 Concurrent Dispatch Queue

  • Serial Dispatch Queue 需要等待 Queue 中的上一个任务执行完毕才会继续执行下一个任务;只会使用一个线程
  • Concurrent Dispatch Queue 并发的同时执行多个任务;会同时使用多个线程,具体使用的线程数量由 XNU 内核根据资源情况决定并控制生成和结束

由于 Serial Dispatch Queue 会严格控制执行的顺序,所以每一个 Serial Dispatch Queue 都会被单独创建一个对应的线程;因此在大量使用 Serial Dispatch Queue 会因为创建了过多的线程影响性能

Main Dispatch Queue

Main Dispatch Queue 是在主线程中执行的 Dispatch Queue;因为主线程只有一个,所以 Main Dispatch QueueSerial Dispatch Queue

所有添加到 Main Dispatch Queue 的任务都是在主线程的 Runloop 中执行

Global Dispatch Queue

Global Dispatch Queue系统的全局并发队列,所有的 App 都可以获取到它

Global Dispatch Queue 有 4 个优先级

  • High Priority
  • Default Priority
  • Low Priority
  • Background Priority

XNU 内核会使用队列的优先级作为线程的执行优先级。但是由于用于处理 Global Dispatch Queue 的线程并不能保证实时性,所以线程优先级只是大致的判断,并不准确

自定义的 Dispatch Queue

使用 dispatch_queue_create 函数生成的 Queue;无论是 Serial Dispatch Queue 还是 Concurrent Dispatch Queue,都使用与默认优先级的 Global Dispatch Queue 相同优先级的线程处理

GCD 中的对象需要手动进行内存管理,所以自定义的 Dispatch Queue 需要通过 dispatch_retaindisptach_release 管理引用计数

dispath_set_target_queue

dispath_set_target_queue 函数可以改变 Dispatch Queue 的优先级

实现

Dispatch Queue 的管理和调度是通过底层的 Libc pthread_workqueue 和内核中的 XNU workqueue

GCD queue

libdispatch

libdispatch 层,主要有 Main Dispatch QueueGlobal Dispatch Queue 两种队列

其中 Main Dispatch Queue 通过主线程 Runloop 执行任务,不需要内核进行调度

Global Dispatch Queue 则按照优先级以及是否并发分为 8 种:

  • Global Dispatch Queue (High Priority)
  • Global Dispatch Queue (Default Priority)
  • Global Dispatch Queue (Low Priority)
  • Global Dispatch Queue (Background Priority)
  • Global Dispatch Queue (High Overcommit Priority)
  • Global Dispatch Queue (Default Overcommit Priority)
  • Global Dispatch Queue (Low Overcommit Priority)
  • Global Dispatch Queue (Background Overcommit Priority)

优先级中带有 Overcommit 表示用于 Serial Dispatch Queue,在 Serial Dispatch Queue 使用时,无论系统状态如何,都会强制生成新的线程处理 Dispatch Queue

Libc (pthreads)

libdispatch 中的每一个种 Global Dispatch Queue 各自使用一个 Libc 中的 pthread_workqueue

  1. 在 GCD 初始化时,通过 pthread_workqueue_create_np 生成 pthread_workqueue
  2. 通过 pthread_workqueuebsdthread_registerworkq_open 可以在 XNU 内核初始化 workqueue 之后获取 workqueue 的信息

XNU 内核

XNU 内核中也有 4 种优先级的 workqueue

  • WORKQUEUE_HIGH_PRIOQUEUE
  • WORKQUEUE_DEFAULT_PRIOQUEUE
  • WORKQUEUE_LOW_PRIOQUEUE
  • WORKQUEUE_BG_PRIOQUEUE

这四种优先级与 Dispatch Queue 中的优先级相同;它们之间的对应关系如下

priority

执行过程

当在 Global Dispatch Queue 中执行任务时,完整的执行流程如下:

  1. libdispatchGlobal Dispatch Queue 队列中取出 Dispatch Continuation 包装的任务
  2. Global Dispatch Queue 自身、符合其优先级的 workqueue 信息以及 Dispatch Continuation 的回调函数等作为参数,调用 pthread_workqueue_additem_np 函数
  3. pthread_workqueue_additem_np 函数使用 workq_kernreturn 系统调用,根据传入的 workqueue 信息找到对应优先级的 workqueue,通知 workqueue 增加应当执行的任务
  4. 收到通知的 XNU 内核基于系统状态判断是否要生成新的线程。如果是 OvercommitGlobal Dispatch Queue,则 workqueue 始终生成新的线程
    • workqueue 中生成的线程只在 workqueue 的线程计划表中运行,相当于线程只用于对应优先级的任务。所以与一般的线程上下文切换不同,性能会更好
  5. workqueue 的线程接着调用 pthread_workqueue 函数,pthread_workqueue 函数会调用 libdispatch 中的回调函数,在该回调函数中执行 Dispatch Continuation 中的任务
  6. 任务执行完毕后,进行通知 Dispatch Group 任务结束、释放 Dispatch Continuation 等处理;执行 Queue 中的下一个任务

自定义 Dispatch Queue 执行

上面说明了 Main Dispatch QueueGlobal Dispatch Queue 的执行原理。那如果是自定义的 Dispatch Queue 是怎么执行的呢?

自定义的 Dispatch Queue 都有一个执行目标 Queue,最终都是通过执行目标 Queue 执行 Queue 中的任务;理所当然的,这些执行目标 Queue 就只能是 Main Dispatch QueueGlobal Dispatch Queue 中的其中一个

创建的 Queue 默认使用 Default 优先级的 Global Queue 作为目标;可以使用 dispatch_queue_create_with_target 方法在创建时指定目标 Queue

再谈 dispath_set_target_queue

dispath_set_target_queue 函数不仅可以改变 Dispatch Queue 的优先级;还可以改变 Queue 的目标执行 Queue。

如果在多个 Serial Dispatch Queue 中用 dispath_set_target_queue 函数指定执行目标为同一个 Serial Dispatch Queue;那么原本应该并行执行的多个 Serial Dispatch Queue 只能在目标 Serial Dispatch Queue 上串行执行

其他 GCD 功能简介

dispatch_after

延迟在指定 Queue 执行任务;dispatch_after 实质不是在指定时间之后执行任务,而是在指定时间之后将任务添加到指定的 Queue 立即执行

定时添加任务的定时器是通过 Runloop 实现的,所以时间同样不会十分精确

dispatch_time 用于获取相对时间;dispatch_walltime 用于获取绝对时间

dispatch_group

waiting …

dispatch_barrier_

dispatch_apply

dispatch_apply 类似于 dispatch_sync,会等待所有函数执行完毕

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2017-2021 HonQi

请我喝杯咖啡吧~