iOS多线程——GCD底层探索上

准备

开源的libdispatch下载地址

  • 创建4个队列,并打印查看每个队列
// 创建一个串行队列 
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
// 创建一个并发队列
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
// 获取主队列
dispatch_queue_t main = dispatch_get_main_queue();
// 获取全局并发队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
NSLog(@"%@-%@-%@-%@",queue1 ,queue2,main,globalQueue);

打印结果:

请添加图片描述

如何查找 GCD 源码

来到工程,我们跳转查看dispatch_async如下:只是提供了一个对外的接口。

#ifdef __BLOCKS__
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
#endif

所以我们再去断点调试:
请添加图片描述

在两个符号断点dispatch_queue_create 和dispatch_sync
如图所示
请添加图片描述

我们在dispatch_async下断点,查看堆栈下的dispatch_async,就会来到汇编调用情况,其中我们可以看到libdispatch.dylib 调用了dispatch_async:,所以根据以前的经验,GCD的库就在libdispatch.dylib 里面.所以我们去源码中查看. 下载地址上面已给出。

libdispatch源码分析

当我翻开源码的时候,首先我发现,没法编译这份源码,其次源码中结构体、方法等的命名不能从字面理解,各种宏定义的嵌套错综复杂,所以看了半天有点读不下去了。但是,虽然不能像以前探索objc源码那样通过LLDB调试验证,但是我们可以从我们使用GCD流程下手,刨根问底,抱着探索求真的目的,顺着思路往下慢慢的找关键代码,然后进行分析其流程。

队列dispatch_queue_t

1.源码中全局去搜索找找dispatch_queue_t****

首先可以看到dispatch_queue_t本身只是dispatch_queue_s这个结构体指针

typedef struct dispatch_queue_s *dispatch_queue_t; 

2.继续查找dispatch_queue_s定义(搜索它dispatch_queue_s {)

struct dispatch_queue_s {
    DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;

3. 继续查找DISPATCH_QUEUE_CLASS_HEADER ,发现是个宏如下

#define DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
    _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__); \
    /* LP64 global queue cacheline boundary */ \
    unsigned long dq_serialnum; \// queue的编号
    const char *dq_label; \   //标签
    DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \
        const uint16_t dq_width, \
        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

4. 宏里我们找到相关的方法_DISPATCH_QUEUE_CLASS_HEADER(x, pointer_sized_field); 继续展开搜索查看里面的内容如下

// 展开_DISPATCH_QUEUE_CLASS_HEADER

#define _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
    DISPATCH_OBJECT_HEADER(x); \
    DISPATCH_UNION_LE(uint64_t volatile dq_state, \
            dispatch_lock dq_state_lock, \
            uint32_t dq_state_bits \
    ); \

// 持续展开DISPATCH_OBJECT_HEADER

#define DISPATCH_OBJECT_HEADER(x) \
    struct dispatch_object_s _as_do[0]; \
    _DISPATCH_OBJECT_HEADER(x)
    
// 进一步查看 _DISPATCH_OBJECT_HEADER

#define _DISPATCH_OBJECT_HEADER(x) \
    struct _os_object_s _as_os_obj[0]; \
    OS_OBJECT_STRUCT_HEADER(dispatch_##x); \  // 这个宏,可以理解为dispatch_object_s继承自_os_object_s
    struct dispatch_##x##_s *volatile do_next; \
    struct dispatch_queue_s *do_targetq; \
    void *do_ctxt; \
    void *do_finalizer
    

来到OS_OBJECT_STRUCT_HEADER之后,我们需要注意一个成员变量,记住这个成员变量名字叫做do_vtable。再继续拆解_OS_OBJECT_HEADER发现里面起就是一个isa指针和引用计数一些信息。

5. 再查看 OS_OBJECT_STRUCT_HEADER

#define OS_OBJECT_STRUCT_HEADER(x) \
    _OS_OBJECT_HEADER(\
    const void *_objc_isa, \
    do_ref_cnt, \
    do_xref_cnt); \
// 注意这个成员变量,后面将任务Push到队列就是通过这个变量
    const struct x##_vtable_s *do_vtable
    
// 进一步查看 _OS_OBJECT_HEADER

#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \
        isa; /* must be pointer-sized */ \ // isa指针
        int volatile ref_cnt; \ // gcd对象内部引用计数
        int volatile xref_cnt// gcd对象外部引用计数(内外部都要减到0时,对象会被释放)

经过一步步相关代码的查找最终来到_OS_OBJECT_HEADER ,可以发现这些代码晦涩难懂,但是我们研究源码不一定要知道每句代码的意思,我们查看其核心内容:

dispatch_queue_t 的本质是一个结构体指针对象,

里面包含了label(标签)、priority(优先级)等一些信息。

dispatch_queue_t类型的指针,指向一个dispatch_queue_s 类型的结构体。

其实GCD源码中的数据结构为dispatch_object_t联合抽象类

typedef union {
    struct _os_object_s *_os_obj;// 基类
    struct dispatch_object_s *_do;// 基类继承os_object
    struct dispatch_queue_s *_dq;// 队列结构
    struct dispatch_queue_attr_s *_dqa;// 队列相关属性
    struct dispatch_group_s *_dg;// group结构
    struct dispatch_source_s *_ds;
    struct dispatch_channel_s *_dch;
    struct dispatch_mach_s *_dm;
    struct dispatch_mach_msg_s *_dmsg;
    struct dispatch_semaphore_s *_dsema;// 信号量
    struct dispatch_data_s *_ddata;
    struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;

请添加图片描述

创建队列 dispatch_queue_create

1.源码中全局去搜索找dispatch_queue_create

当我们搜索dispatch_queue_create有很多引用,我们可以直接搜索dispatch_queue_create(con

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);
}
  • label : 标签,我们平时传入队列的名字 attr :我们知道创建队列时, attr
  • 属性有三个值可选,nil、DISPATCH_QUEUE_SERIAL(实际上就是 nil) 或
  • DISPATCH_QUEUE_CONCURRENT

2.搜索_dispatch_lane_create_with_target(函数

这里会创建一个root队列,并将自己新建的队列绑定到所对应的root队列上。

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)
{
	// dqai 创建 - dqa = 决定串行还是并行
	/*
	 // 根据上文代码注释里提到的,作者认为调用者传入DISPATCH_QUEUE_SERIAL和nil的几率要大于传DISPATCH_QUEUE_CONCURRENT。所以这里设置个默认值。
		// 这里怎么理解呢?只要看做if(!dqa)即可
		if (!slowpath(dqa)) {
			// _dispatch_get_default_queue_attr里面会将dqa的dqa_autorelease_frequency指定为DISPATCH_AUTORELEASE_FREQUENCY_INHERIT的,inactive也指定为false。这里就不展开了,只需要知道赋了哪些值。因为后面会用到。
			dqa = _dispatch_get_default_queue_attr();
		} else if (dqa->do_vtable != DISPATCH_VTABLE(queue_attr)) {
			DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
		}
	 */
	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;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
	if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
		dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
	}
	if (qos == DISPATCH_QOS_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) {
		if (tq->do_targetq) {
			DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
					"a non-global target queue");
		}
	}

	if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
		// Handle discrepancies between attr and target queue, attributes win
		if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
			if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
				overcommit = _dispatch_queue_attr_overcommit_enabled;
			} else {
				overcommit = _dispatch_queue_attr_overcommit_disabled;
			}
		}
		if (qos == DISPATCH_QOS_UNSPECIFIED) {
			qos = _dispatch_priority_qos(tq->dq_priority);
		}
		tq = NULL;
	} else if (tq && !tq->do_targetq) {
		// target is a pthread or runloop root queue, setting QoS or overcommit
		// is disallowed
		if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
			DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
					"and use this kind of target queue");
		}
	} else {
		if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
			// Serial queues default to overcommit!
			overcommit = dqai.dqai_concurrent ?
					_dispatch_queue_attr_overcommit_disabled :
					_dispatch_queue_attr_overcommit_enabled;
		}
	}
	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");
		}
	}

	//
	// Step 2: Initialize the queue
	//

	if (legacy) {
		// if any of these attributes is specified, use non legacy classes
		if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
			legacy = false;
		}
	}

	// -------  开始进行队列的创建 --------
	// vtable变量很重要,之后会被赋值到之前说的dispatch_queue_t结构体里的do_vtable变量上
	const void *vtable;
	dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
	if (dqai.dqai_concurrent) {
		// OS_dispatch_##name  == OS_dispatch_queue_concurrent
		vtable = DISPATCH_VTABLE(queue_concurrent);
	} else {
		// 如果传入nil/ null/0 都会创建一个串行队列
		// OS_dispatch_queue_serial
		vtable = DISPATCH_VTABLE(queue_serial);
	}
	switch (dqai.dqai_autorelease_frequency) {
	case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
		dqf |= DQF_AUTORELEASE_NEVER;
		break;
	case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
		dqf |= DQF_AUTORELEASE_ALWAYS;
		break;
	}
	if (label) {
		const char *tmp = _dispatch_strdup_if_mutable(label);
		if (tmp != label) {
			dqf |= DQF_LABEL_NEEDS_FREE;
			label = tmp;
		}
	}
    // _dispatch_object_alloc里面就将vtable赋值给do_vtable变量上了。
	dispatch_lane_t dq = _dispatch_object_alloc(vtable,
			sizeof(struct dispatch_lane_s));
	// 第三个参数根据是否并行队列,如果不是则最多开一个线程,如果是则最多开0x1000 - 2个线程,这个数量很惊人了已经,换成十进制就是(4096 - 2)个。
    // dqa_inactive之前说串行是false的
	// DISPATCH_QUEUE_ROLE_INNER 也是0,所以这里串行队列的话dqa->dqa_state是0
	_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
			DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
			(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

	dq->dq_label = label;
	dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
			dqai.dqai_relpri);
	if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
		dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
	}
	if (!dqai.dqai_inactive) {
		_dispatch_queue_priority_inherit_from_target(dq, tq);
		_dispatch_lane_inherit_wlh_from_target(dq, tq);
	}
	_dispatch_retain(tq);
	// 自定义的queue的目标队列是root队列
	dq->do_targetq = tq;
	_dispatch_object_debug(dq, "%s", __func__);
	return _dispatch_trace_queue_create(dq)._dq;
}

请添加图片描述

在源码中就会发现返回值是dq那么我们分析dq的创建即可

3.点击进入 _dispatch_object_alloc源码查看

_dispatch_object_alloc: 申请对应类型的内存;

void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
	const struct dispatch_object_vtable_s *_vtable = vtable;
	dispatch_object_t dou;
	dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
	dou._do->do_vtable = vtable;
	return dou._do;
#else
	return _os_object_alloc_realized(vtable, size);
#endif
}

我们只需要查看OS_OBJECT_HAVE_OBJC2的内容,即else中**_os_object_alloc_realized的源码**。

3. 点击进入 _os_object_alloc_realized 源码查看

_dispatch_queue_init:初始化。

inline _os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
	_os_object_t obj;
	dispatch_assert(size >= sizeof(struct _os_object_s));
	while (unlikely(!(obj = calloc(1u, size)))) {
		_dispatch_temporary_resource_shortage();
	}
	obj->os_obj_isa = cls;
	return obj;
}

1.在源码中我们发现队列的ISA指向的cls。
2.那么向上查看传入参数cls是在哪创建传入的,结果发现是在_dispatch_lane_create_with_target中创建的。即变量vtable

_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)
		{
		
		......
		
		const void *vtable;
	dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
	// 判断是串行还是并发队列
	if (dqai.dqai_concurrent) {
		// OS_dispatch_queue_concurrent
		vtable = DISPATCH_VTABLE(queue_concurrent);
	} else {
		vtable = DISPATCH_VTABLE(queue_serial);
	}
	
	......
	
	dispatch_lane_t dq = _dispatch_object_alloc(vtable,
			sizeof(struct dispatch_lane_s)); // alloc
	_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
			DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
			(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
			......
		}


并发队列和串行队列的区别

// 判断是串行还是并发队列
	if (dqai.dqai_concurrent) {
		// OS_dispatch_queue_concurrent
		vtable = DISPATCH_VTABLE(queue_concurrent);
	} else {
		vtable = DISPATCH_VTABLE(queue_serial);
	}
  1. 全局搜索DISPATCH_VTABLE,发现定义的DISPATCH_OBJC_CLASS
#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)

-2.全局搜索DISPATCH_OBJC_CLASS,是定义的DISPATCH_CLASS_SYMBOL

#define DISPATCH_OBJC_CLASS(name)	(&DISPATCH_CLASS_SYMBOL(name))

3.全局搜索DISPATCH_CLASS_SYMBOL,然后发现queue的名字是使用OS_dispatch_…拼接的名字。因此在创建时,并发队列使用的是queue_concurrent,串行队列使用的是queue_serial字符串。

#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class
#define DISPATCH_CLASS(name) OS_dispatch_##name

输出打印串行、并行队列,验证其队列的名称

请添加图片描述

总结

queue是一个对象。
queue创建流程:

dispatch_queue_create => _dispatch_object_alloc =>
_os_object_alloc_realized

DISPATCH_QUEUE_WIDTH_POOL 是并发队列的最大容积 – 4095
DISPATCH_QUEUE_WIDTH_MAX是普通队列的最大容积 – 4094

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值