变态的libDispatch结构分析-全局队列同步任务处理过程

13 篇文章 0 订阅
8 篇文章 0 订阅

GCD的处理过程,从全局队列的同步处理任务开始。

先说明一下两个宏定义

#if __GNUC__
#define fastpath(x)	((typeof(x))__builtin_expect((long)(x), ~0l))
#define slowpath(x)	((typeof(x))__builtin_expect((long)(x), 0l))


# define __builtin_expect(expr, val) (expr);
其实就是返回expr;

1. 进入sync中

调用   dispatch_sync_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), reinterpret_cast<void *>(item), _WorkItemRunner);

1.1. 队列回顾

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)获取到的GlobalQueue结构:

	{
		.do_vtable = &_dispatch_queue_root_vtable,
		.do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
		.do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT,
		.do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK,
		.do_ctxt = &_dispatch_root_queue_contexts[2],

		.dq_label = "com.apple.root.default-priority",
		.dq_running = 2,
		.dq_width = UINT32_MAX,//# define UINT32_MAX        (4294967295U)
		.dq_serialnum = 6,
	},


static const struct dispatch_queue_vtable_s _dispatch_queue_root_vtable = {
	.do_type = DISPATCH_QUEUE_GLOBAL_TYPE,
	.do_kind = "global-queue",
	.do_debug = dispatch_queue_debug,
	.do_probe = _dispatch_queue_wakeup_global,
};
_dispatch_queue_wakeup_global 是WakeUp的关键函数,带回儿会详细讲解;


1.2. dispatch_sync_f

同步任务执行的方法接口是dispatch_sync_f

void
dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
	typeof(dq->dq_running) prev_cnt;
	dispatch_queue_t old_dq;
state1
	if (dq->dq_width == 1) {
		return dispatch_barrier_sync_f(dq, ctxt, func);
	}
state2
	// 1) ensure that this thread hasn't enqueued anything ahead of this call
	// 2) the queue is not suspended
	if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))) {
		_dispatch_sync_f_slow(dq);
	} else {
state3		prev_cnt = dispatch_atomic_add(&dq->dq_running, 2) - 2;

		if (slowpath(prev_cnt & 1)) {
			if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) {
				_dispatch_wakeup(dq);
			}
			_dispatch_sync_f_slow(dq);
		}
	}
state4
	old_dq = _dispatch_thread_getspecific(dispatch_queue_key);
	_dispatch_thread_setspecific(dispatch_queue_key, dq);
	func(ctxt);
	_dispatch_workitem_inc();
	_dispatch_thread_setspecific(dispatch_queue_key, old_dq);

	if (slowpath(dispatch_atomic_sub(&dq->dq_running, 2) == 0)) {
		_dispatch_wakeup(dq);
	}
}

state1


会检查队列允许的并行宽度,若宽度为1,则进入dispatch_barrier_sync_f;

通常private Queue才会设置dq_width为1,保证private queue的FIFO次序。 

	if (dq->dq_width == 1) {  //判定可以并行运行的任务数
		return dispatch_barrier_sync_f(dq, ctxt, func);
	}
GlobelQueue的Width是MAX值,因此不会进入到上面的代码中;接下来进入到state2;

state2
slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq)

这段代码的或语句的左边判定dq是否存在尾部节点,其实就是判定是否存在任务;

右边的语句,判定dq是不是设置了suspend暂停标记,这个通常用于延时任务;

我们假设第一次插入任务到队列中,那么跳过state2进入state3;


state3

    } else {
        prev_cnt = dispatch_atomic_add(&dq->dq_running, 2) - 2;

        if (slowpath(prev_cnt & 1)) {
            if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) {
                _dispatch_wakeup(dq);
            }
            _dispatch_sync_f_slow(dq);
        }
    }
先通过原子操作将dq_running+2,dq_running表示正在运行的任务数;但为什么要+2啊?

然后判定之前的dq_running的值prev_cnt,若prev_cnt&1!=0,那么进入到上面的if语句中;

这里有个小地方,从目前的代码来看,每次dq_running都是加2,那么若&1不等于0,应该是其他处理过程中设置的;总之在我们这里是不成立的;

而GlobelQueue的prev_cnt为2;因此跳入到state4;

state4

	old_dq = _dispatch_thread_getspecific(dispatch_queue_key);
	_dispatch_thread_setspecific(dispatch_queue_key, dq);
	func(ctxt);
	_dispatch_workitem_inc();
	_dispatch_thread_setspecific(dispatch_queue_key, old_dq);

	if (slowpath(dispatch_atomic_sub(&dq->dq_running, 2) == 0)) {
		_dispatch_wakeup(dq);
	}


通过dispatch_queue_key获取线程中的私有数据,参看:phtread_key_t介绍

然后将dq保存到线程私有变量上去,说明目前用的queue就是dq;

运行了func(ctext):执行了函数方法,任务在此执行;

然后运行dispatch_atomic_sub(&dq->dq_running, 2),若发现dq_running变成了0,则唤醒dq;这个逻辑需要后面分析才能知道原因了。

总之在全局队列中,这个dq_running在执行同步的时候,是不会存在dq_running-2 =0的情况;

那么同步任务执行就结束了。











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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值