【iOS 底层】GCD dispatch_queue_create源码分析

原创
2021/01/10 22:08
阅读数 589

Pre

源码分析版本:libdispatch-1173.100.2

开始分析之前先带着两个问题

  1. 队列是如何产生的?
  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)

以下分析该函数流程:

  1. 初始化队列信息结构体。 将队列属性传入_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用于区分串行队列和并发队列。

  1. 规范化参数(qos, overcommit, tq)

    • qos:优先级
    • overcommit:是否支持过载
    • tq:目标队列
  2. 初始化队列

    • 经过一系列宏定义的嵌套可以看到,队列类型是由字符串拼接而来OS_dispatch_##name##_class,其中name为queue_concurrentqueue_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、优先级等。

  1. 再次封装,设置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

验证一下 完美

流程总结

  1. 初始化队列信息结构体
  2. 规范化参数(qos, overcommit, tq)
  3. 初始化队列,开辟内存,属性设置
  4. 队列再封装
展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部
返回顶部
顶部