在 GCD 中使用最多的三种队列:主队列(dispatch_get_main_queue()
)、全局并发队列(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
)、自定义队列(dispatch_queue_create
),那么我们就先由创建自定义队列开始学习。
dispatch_queue_create(创建自定义队列)
下面就沿着源码一路看队列的创建过程。
// 创建一个并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent", DISPATCH_QUEUE_CONCURRENT);
// 创建一个串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
DISPATCH_QUEUE_SERIAL
用于创建以 FIFO 顺序串行调用块的调度队列(串行队列)的属性,值是 NULL
。
#define DISPATCH_QUEUE_SERIAL NULL
DISPATCH_QUEUE_CONCURRENT
可用于创建调度队列(并发队列)的属性,该调度队列可同时调用块并支持通过调度屏障 API (dispatch_barrier_async
)提交的屏障块。(常规 block 和 barrier 的 block 任务块)
#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))
#define DISPATCH_QUEUE_CONCURRENT \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, _dispatch_queue_attr_concurrent)
API_AVAILABLE(macos(10.7), ios(4.3))
DISPATCH_EXPORT
struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent; // 这里有一个 dispatch_queue_attr_s 结构体类型的全局变量。
DISPATCH_QUEUE_CONCURRENT
宏定义是把全局变量 _dispatch_queue_attr_concurrent
强制转化为了 dispatch_queue_attr_t
类型的变量。
dispatch_queue_create
函数的实现。label
参数是要附加到队列的自定义的字符串标签,attr
参数是预定义属性,DISPATCH_QUEUE_SERIAL
、DISPATCH_QUEUE_CONCURRENT
或调用 dispatch_queue_attr_make_with_*
函数的自定义创建的 dispatch_queue_attr_t
结构体实例。
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) {
return _dispatch_lane_create_with_target(label, attr, DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
dispatch_queue_create
函数内部调用了一个中间函数 _dispatch_lane_create_with_target
,其中用了一个 DISPATCH_TARGET_QUEUE_DEFAULT
作为默认参数。
DISPATCH_TARGET_QUEUE_DEFAULT
DISPATCH_TARGET_QUEUE_DEFAULT
是传递给 dispatch_queue_create_with_target
、dispatch_set_target_queue
和 dispatch_source_create
函数的常量,以指示应使用(相关对象类型的)默认目标队列,它的实际值是 NULL
。
#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
dispatch_lane_t
dispatch_lane_t
是指向 dispatch_lane_s
结构体的指针。
typedef struct dispatch_lane_s {
DISPATCH_LANE_CLASS_HEADER(lane);
/* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;
DISPATCH_LANE_CLASS_HEADER
#define DISPATCH_LANE_CLASS_HEADER(x) \
struct dispatch_queue_s _as_dq[0]; \
DISPATCH_QUEUE_CLASS_HEADER(x, \
struct dispatch_object_s *volatile dq_items_tail); \
dispatch_unfair_lock_s dq_sidelock; \
struct dispatch_object_s *volatile dq_items_head; \
uint32_t dq_side_suspend_cnt
把 dispatch_lane_s
定义中的宏完全展开的话:
typedef struct dispatch_lane_s {
// 此处两行则是 dispatch_lane_s 继承的父类 dispatch_queue_s 的头部内容
struct dispatch_queue_s _as_dq[0];
struct dispatch_object_s _as_do[0];
struct _os_object_s _as_os_obj[0];
const struct dispatch_lane_vtable_s *do_vtable; /* must be pointer-sized */
int volatile do_ref_cnt;
int volatile do_xref_cnt;
struct dispatch_lane_s *volatile do_next;
struct dispatch_queue_s *do_targetq;
void *do_ctxt;
void *do_finalizer
struct dispatch_object_s *volatile dq_items_tail;
union {
uint64_t volatile dq_state;
struct {
dispatch_lock dq_state_lock;
uint32_t dq_state_bits;
};
};
/* LP64 global queue cacheline boundary */
unsigned long dq_serialnum;
const char *dq_label;
union {
uint32_t volatile dq_atomic_flags;
struct {
const uint16_t dq_width; // 队列的宽度(串行队列为 1,并发队列大于 1)
const uint16_t __dq_opaque2;
};
};
dispatch_priority_t dq_priority;
union {
struct dispatch_queue_specific_head_s *dq_specific_head;
struct dispatch_source_refs_s *ds_refs;
struct dispatch_timer_source_refs_s *ds_timer_refs;
struct dispatch_mach_recv_refs_s *dm_recv_refs;
struct dispatch_channel_callbacks_s const *dch_callbacks;
};
int volatile dq_sref_cnt;
dispatch_unfair_lock_s dq_sidelock; // 锁
struct dispatch_object_s *volatile dq_items_head; // 头
uint32_t dq_side_suspend_cnt // 挂起次数
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;
这里有一个iOS交流圈有兴趣的可以了解一下:891 488 181 分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!
可看到 dispatch_lane_s
是继承自 dispatch_queue_s
的“子类”,且 _dispatch_lane_create_with_target
函数返回的正是 dispatch_lane_s
而不是 dispatch_queue_s
类型。
DISPATCH_QUEUE_WIDTH_MAX
#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull //(4096)为创建全局队列时候所使用的
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1) // 0xfffull(4095)
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2) // 0xffeull // 队列宽度的最大值 (4094)
_dispatch_priority_make
优先级及相对量。
#define _dispatch_priority_make(qos, relpri) \
(qos ? ((((qos) << DISPATCH_PRIORITY_QOS_SHIFT) & DISPATCH_PRIORITY_QOS_MASK) | \
((dispatch_priority_t)(relpri - 1) & DISPATCH_PRIORITY_RELPRI_MASK)) : 0)
_dispatch_lane_create_with_target
函数实现:
DISPATCH_NOINLINE
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 函数上篇我们讲解过,
// 1\. 如果 dqa 是 DISPATCH_QUEUE_SERIAL(值是 NULL)作为入参传入的话,
// 会直接返回一个空的 dispatch_queue_attr_info_t 结构体实例,(dispatch_queue_attr_info_t dqai = { };)。
// 2\. 如果 dqa 是 DISPATCH_QUEUE_CONCURRENT(值是全局变量 _dispatch_queue_attr_concurrent)作为入参传入的话,
// 会返回一个 dqai_concurrent 值是 true 的 dispatch_queue_attr_info_t 结构体实例,(dqai_concurrent 为 true 表示是并发队列)。
// 3\. 第三种情况则是传入自定义的 dispatch_queue_attr_t 时,
// 则会进行取模和取商运算为 dispatch_queue_attr_info_t 结构体实例的每个成员变量赋值后返回该 dispatch_queue_attr_info_t 结构体实例。
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
//
// Step 1: Normalize arguments (qos, overcommit, tq) 规范化参数
//
dispatch_qos_t qos = dqai.dqai_qos; //(dqai_qos 表示线程优先级)
// 如果 HAVE_PTHREAD_WORKQUEUE_QOS 为假会进行一个 dqai_qos 的切换
#if !HAVE_PTHREAD_WORKQUEUE_QOS
if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
// 如果是 "用户交互" 这个最高优先级,则切到 "用户启动" 这个第二优先级
dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
}
if (qos == DISPATCH_QOS_MAINTENANCE) {
// 如果是 "QOS_CLASS_MAINTENANCE" 这个最低优先级,则切到 "后台线程" 这个倒数第二优先级
dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
}
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS
// 取出是否允许 "过量使用(超过物理上的核心数)"
_dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
// 如果 overcommit 不等于 "未指定 overcommit" 并且 tq 不为空