Pre
源码分析版本:libdispatch-1173.100.2
开始分析之前先带着两个问题
- 队列是如何产生的?
- 串行队列和并发队列在底层结构和实现中有什么区别?
队列创建流程
dispatch_queue_create
方法官方注释
/*!
* @function dispatch_queue_create
*
* @abstract
* Creates a new dispatch queue to which blocks may be submitted.
*
* @discussion
* Dispatch queues created with the DISPATCH_QUEUE_SERIAL or a NULL attribute
* invoke blocks serially in FIFO order.
*
* Dispatch queues created with the DISPATCH_QUEUE_CONCURRENT attribute may
* invoke blocks concurrently (similarly to the global concurrent queues, but
* potentially with more overhead), and support barrier blocks submitted with
* the dispatch barrier API, which e.g. enables the implementation of efficient
* reader-writer schemes.
*
* When a dispatch queue is no longer needed, it should be released with
* dispatch_release(). Note that any pending blocks submitted asynchronously to
* a queue will hold a reference to that queue. Therefore a queue will not be
* deallocated until all pending blocks have finished.
*
* Passing the result of the dispatch_queue_attr_make_with_qos_class() function
* to the attr parameter of this function allows a quality of service class and
* relative priority to be specified for the newly created queue.
* The quality of service class so specified takes precedence over the quality
* of service class of the newly created dispatch queue's target queue (if any)
* as long that does not result in a lower QOS class and relative priority.
*
* When no quality of service class is specified, the target queue of a newly
* created dispatch queue is the default priority global concurrent queue.
*
* @param label
* A string label to attach to the queue.
* This parameter is optional and may be NULL.
*
* @param attr
* A predefined attribute such as DISPATCH_QUEUE_SERIAL,
* DISPATCH_QUEUE_CONCURRENT, or the result of a call to
* a dispatch_queue_attr_make_with_* function.
*
* @result
* The newly created dispatch queue.
/ * !
* @function dispatch_queue_create
*
* @abstract
*创建一个新的可以提交block的dispatch queue。
** @discussion
*使用DISPATCH_QUEUE_SERIAL或NULL属性创建的调度队列按FIFO顺序序列化阻塞。
*使用DISPATCH_QUEUE_CONCURRENT属性创建的调度队列可以并发调用block(类似于全局并发队列,但可能会有更多的开销),并且支持通过Dispatch barrier API提交的barrier block,这使得高效的读写方案能够实现。
*
当调度队列不再需要时,使用dispatch_release()释放它。注意,任何异步提交到队列的挂起块都将保存对该队列的引用。因此,在所有挂起的块完成之前,不会释放队列。
*
*将dispatch_queue_attr_make_with_qos_class()函数的结果传递给该函数的attr参数,允许为新创建的队列指定服务质量类和相对优先级。
*指定的服务类的质量优先于新创建的调度队列的目标队列(如果有)的服务类的质量,只要它不会导致较低的QOS类和相对优先级。
*
*当没有指定服务质量类时,新创建的目标队列
*创建的调度队列是默认优先级全局并发队列。
*
* @param标签
*附加到队列的字符串标签。
*可选参数,可以为空。
*
* @param attr
*预定义的属性,如DISPATCH_QUEUE_SERIAL、DISPATCH_QUEUE_CONCURRENT或调用dispatch_queue_attr_make_with_*函数的结果。
* @result
*新创建的调度队列。
* /
*/
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT
DISPATCH_NOTHROW
dispatch_queue_t
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);
跳转至函数实现可以看到实际调用的是:
//tq -> DISPATCH_TARGET_QUEUE_DEFAULT
#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
//legacy -> true
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
以下分析该函数流程:
- 初始化队列信息结构体。 将队列属性传入
_dispatch_queue_attr_to_info(dispatch_queue_attr_t)
方法返回一个队列信息的结构体dispatch_queue_attr_info_t dqai
typedef struct dispatch_queue_attr_info_s {
dispatch_qos_t dqai_qos : 8;
int dqai_relpri : 8;
uint16_t dqai_overcommit:2;
uint16_t dqai_autorelease_frequency:2;
uint16_t dqai_concurrent:1;
uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;
在该方法中,若attr为空(即我们创建串行队列时常传入的NULL)则返回一个空结构体。不为空则会用苹果自己的一套复杂缠绕我反正看不懂的算法(位域相关)对dqai基本属性进行一些设置。结构体中的dqai_concurrent用于区分串行队列和并发队列。
-
规范化参数(qos, overcommit, tq)
qos
:优先级overcommit
:是否支持过载tq
:目标队列
-
初始化队列
-
经过一系列宏定义的嵌套可以看到,队列类型是由字符串拼接而来
OS_dispatch_##name##_class
,其中name为queue_concurrent
或queue_serial
。 -
开辟内存,生成相应的对象
_dispatch_object_alloc
。(是的对象!队列也是一个对象!和NSObject相对应的libdispatch里的根对象为_os_object_t
)_dispatch_object_alloc
->_os_object_alloc_realized
->obj->os_obj_isa = cls;
为该对象设置isa指针指向。 -
构造方法
_dispatch_queue_init
,两个函数初始化出一个队列对象dispatch_lane_t dq
。这里会对队列对象的width属性进行设置,若为并发则width为0xffe;若为串行则width为0x1。
-
//第二个参数如果为并发队列传入DISPATCH_QUEUE_WIDTH_MAX;如果为串行队列传入1
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1,
DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
//也附上DISPATCH_QUEUE_WIDTH_MAX相关宏定义
#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)
代码验证一下
接下来会对dq设置label、优先级等。
- 再次封装,设置target。从
_dispatch_trace_queue_create
进入。
//在dispatch_introspection_queue_get_info函数中会封装成下面这样的对象。
typedef struct dispatch_introspection_queue_s {
dispatch_queue_t queue;
dispatch_queue_t target_queue;
const char *label;
unsigned long serialnum;
unsigned int width;
unsigned int suspend_count;
unsigned long enqueued:1,
barrier:1,
draining:1,
global:1,
main:1;
} dispatch_introspection_queue_s;
//若队列为一般类型
static inline dispatch_introspection_queue_s
_dispatch_introspection_workloop_get_info(dispatch_workloop_t dwl)
{
uint64_t dq_state = os_atomic_load2o(dwl, dq_state, relaxed);
dispatch_introspection_queue_s diq = {
.queue = dwl->_as_dq,
.target_queue = dwl->do_targetq,
.label = dwl->dq_label,
.serialnum = dwl->dq_serialnum,
.width = 1,
.suspend_count = 0,
.enqueued = _dq_state_is_enqueued(dq_state),
.barrier = _dq_state_is_in_barrier(dq_state),
.draining = 0,
.global = 0,
.main = 0,
};
return diq;
}
//若队列为泛型
static inline dispatch_introspection_queue_s
_dispatch_introspection_lane_get_info(dispatch_lane_class_t dqu)
{
dispatch_lane_t dq = dqu._dl;
bool global = _dispatch_object_is_global(dq);
uint64_t dq_state = os_atomic_load2o(dq, dq_state, relaxed);
dispatch_introspection_queue_s diq = {
.queue = dq->_as_dq,
.target_queue = dq->do_targetq,
.label = dq->dq_label,
.serialnum = dq->dq_serialnum,
.width = dq->dq_width,
.suspend_count = _dq_state_suspend_cnt(dq_state) + dq->dq_side_suspend_cnt,
.enqueued = _dq_state_is_enqueued(dq_state) && !global,
.barrier = _dq_state_is_in_barrier(dq_state) && !global,
.draining = (dq->dq_items_head == (void*)~0ul) ||
(!dq->dq_items_head && dq->dq_items_tail),
.global = global,
.main = dx_type(dq) == DISPATCH_QUEUE_MAIN_TYPE,
};
return diq;
}
可以看到对象中有个target_queue
属性从dq->do_targetq
赋值而来,那么这个do_targetq
又是哪来的呢?我们可以在_dispatch_lane_create_with_target
函数中看到这样一段代码
dq->do_targetq = tq;
//而这个tq则来自于
if (!tq) {
tq = _dispatch_get_root_queue(
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
if (unlikely(!tq)) {
DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
}
}
//进入_dispatch_get_root_queue函数看看
static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
···
return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}
可以看出target_queue是从_dispatch_root_queues这个数组中取出。
struct dispatch_queue_global_s _dispatch_root_queues[] = {
_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
.dq_label = "com.apple.root.maintenance-qos",
.dq_serialnum = 4,
),
_DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.maintenance-qos.overcommit",
.dq_serialnum = 5,
),
_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
.dq_label = "com.apple.root.background-qos",
.dq_serialnum = 6,
),
_DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.background-qos.overcommit",
.dq_serialnum = 7,
),
_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
.dq_label = "com.apple.root.utility-qos",
.dq_serialnum = 8,
),
_DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.utility-qos.overcommit",
.dq_serialnum = 9,
),
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 10,
),
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.default-qos.overcommit",
.dq_serialnum = 11,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
.dq_label = "com.apple.root.user-initiated-qos",
.dq_serialnum = 12,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-initiated-qos.overcommit",
.dq_serialnum = 13,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
.dq_label = "com.apple.root.user-interactive-qos",
.dq_serialnum = 14,
),
_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-interactive-qos.overcommit",
.dq_serialnum = 15,
),
};
// 相关宏定义如下
#define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
[_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
.dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
.do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
.dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
_dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
_dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
__VA_ARGS__ \
}
可见_DISPATCH_ROOT_QUEUE_ENTRY第一个参数为字符串,对应优先级; 第二个参数为是否支持过载的flag。拼接而成的字符串DISPATCH_QOS_##n
即为实际优先级。也就是有6种类型:
//主队列,用于更新UI
#define DISPATCH_QOS_MAINTENANCE ((dispatch_qos_t)1)
//用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
#define DISPATCH_QOS_BACKGROUND ((dispatch_qos_t)2)
//需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
#define DISPATCH_QOS_UTILITY ((dispatch_qos_t)3)
//默认队列
#define DISPATCH_QOS_DEFAULT ((dispatch_qos_t)4)
//任务由UI发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
#define DISPATCH_QOS_USER_INITIATED ((dispatch_qos_t)5)
//任务需要被立即执行提供好的体验,用来更新UI,响应事件 等。这个等级最好保持小规模。
#define DISPATCH_QOS_USER_INTERACTIVE ((dispatch_qos_t)6)
那么下标是怎么计算的呢?
//再次收录一下计算相关代码
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
//2 * (qos - 1) + overcommit
&_dispatch_root_queues[2 * (qos - 1) + overcommit];
用串行队列举例,我们在第一步_dispatch_queue_attr_to_info
时曾判断若为串行队列则返回空结构体。那么qos
为4,而这个overcommit
,其实还有一段相关代码
typedef enum {
_dispatch_queue_attr_overcommit_unspecified = 0,
_dispatch_queue_attr_overcommit_enabled,
_dispatch_queue_attr_overcommit_disabled,
} _dispatch_queue_attr_overcommit_t;
overcommit = dqai.dqai_concurrent ?
_dispatch_queue_attr_overcommit_disabled :
_dispatch_queue_attr_overcommit_enabled;
//可见串行队列overcommit为1
//计算:2*(4-1)+1=7 -> com.apple.root.utility-qos.overcommit
验证一下 完美
流程总结
- 初始化队列信息结构体
- 规范化参数(qos, overcommit, tq)
- 初始化队列,开辟内存,属性设置
- 队列再封装