单例dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
这串代码不用解释,相信大家都熟悉。现在前往源码解析
typedef long dispatch_once_t;
这里的once
就是一个long
类型,拿到它的指针类型传入到函数里。
跟着源码走,会进入到这里:
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
if (likely(v == DLOCK_ONCE_DONE)) {
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
if (likely(DISPATCH_ONCE_IS_GEN(v))) {
return _dispatch_once_mark_done_if_quiesced(l, v);
}
#endif
#endif
if (_dispatch_once_gate_tryenter(l)) {
return _dispatch_once_callout(l, ctxt, func);
}
return _dispatch_once_wait(l);
}
第一句代码把我们传进来的指针强转成dispatch_once_gate_t
,进入这个会发现它是一个结构体,里面只有一个联合体。
再往下看的时候,可以先思考一个问题,单例是如何实现只创建一次的?带着这个问题,我们往下走,可以直接跳转到最后
if (_dispatch_once_gate_tryenter(l)) {
return _dispatch_once_callout(l, ctxt, func);
}
从这句代码可以看出,这里是创建它。一步一步来,先看条件
_dispatch_once_gate_tryenter(dispatch_once_gate_t l)
{
// os 对象是否存储过
// unlock
return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
(uintptr_t)_dispatch_lock_value_for_self(), relaxed);
}
说实话,懵逼,代码具体干嘛的不清楚,查资料后,可以理解成判断对象是否在os存储过。然后接着往下走
_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
dispatch_function_t func)
{
_dispatch_client_callout(ctxt, func);
_dispatch_once_gate_broadcast(l);
}
看过我之前文章的小伙伴,看到_dispatch_client_callout()
这个应该很熟悉了,这里就是执行block
里面的内容。创建完之后,就进行下面这个广播的函数
_dispatch_once_gate_broadcast(dispatch_once_gate_t l)
{
dispatch_lock value_self = _dispatch_lock_value_for_self();
uintptr_t v;
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
v = _dispatch_once_mark_quiescing(l);
#else
v = _dispatch_once_mark_done(l);
#endif
if (likely((dispatch_lock)v == value_self)) return;
_dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);
}
仔细看这段代码的意思,第一句拿到当前self
对象,然后拿到一个v
,仔细去看这个v
到底是什么!!!
第一个_dispatch_once_mark_quiescing
表示正在创建,这里标记了一个_dispatch_once_generation()
:
_dispatch_once_mark_quiescing(dispatch_once_gate_t dgo)
{
return os_atomic_xchg(&dgo->dgo_once, _dispatch_once_generation(), release);
}
然后看第二个_dispatch_once_mark_done()
,这里表示标记一个DLOCK_ONCE_DONE
:
_dispatch_once_mark_done(dispatch_once_gate_t dgo)
{
return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
}
此时此刻,是否还记得我们一开始跳过的方法里的代码,看下面代码:
uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
if (likely(v == DLOCK_ONCE_DONE)) {
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
if (likely(DISPATCH_ONCE_IS_GEN(v))) {
return _dispatch_once_mark_done_if_quiesced(l, v);
}
如果标记了获取到的v
等于DLOCK_ONCE_DONE
,就直接返回;如果是下面的,我们在进入查看一下:
_dispatch_once_mark_done_if_quiesced(dispatch_once_gate_t dgo, uintptr_t gen)
{
if (_dispatch_once_generation() - gen >= DISPATCH_ONCE_GEN_SAFE_DELTA) {
/*
* See explanation above, when the quiescing counter approach is taken
* then this store needs only to be relaxed as it is used as a witness
* that the required barriers have happened.
*/
os_atomic_store(&dgo->dgo_once, DLOCK_ONCE_DONE, relaxed);
}
}
不知道走到这里大家是否明白,单例是如何实现只创建一次的。
信号量dispatch_semaphore_t
// 创建信号量对象 信号量 >= 0
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
// -1操作
dispatch_wait(sem, DISPATCH_TIME_FOREVER);
// +1 操作
dispatch_semaphore_signal(sem);
上面三句代码,就是创建信号量的代码。 wait
的-1
操作相当于阻塞操作,signal
则是+1
操作。
进入源码分析:
dispatch_semaphore_create(long value)
{
dispatch_semaphore_t dsema;
if (value < 0) {
return DISPATCH_BAD_INPUT;
}
dsema = _dispatch_object_alloc(DISPATCH_VTABLE(semaphore),
sizeof(struct dispatch_semaphore_s));
dsema->do_next = DISPATCH_OBJECT_LISTLESS;
dsema->do_targetq = _dispatch_get_default_queue(false);
dsema->dsema_value = value;
_dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
dsema->dsema_orig = value;
return dsema;
}
第一步就声明了一个信号量对象,然后判断value
,小于0就是不正确操作。后面就是创建对象,开辟空间。最主要的两步其实就是value
的赋值。
接下来看一下dispatch_wait()
:
#define dispatch_wait(object, timeout) \
_Generic((object), \
dispatch_block_t:dispatch_block_wait, \
dispatch_group_t:dispatch_group_wait, \
dispatch_semaphore_t:dispatch_semaphore_wait \
)((object),(timeout))
这里我们看的是信号量,所以走到dispatch_semaphore_wait
方法里:
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
long value = os_atomic_dec2o(dsema, dsema_value, acquire);
if (likely(value >= 0)) {
return 0;
}
return _dispatch_semaphore_wait_slow(dsema, timeout);
}
中间一个if
条件判断,如果value >= 0
,则直接返回一个0
,返回0
表示堵塞,允许进来的线程为0
;
所以我们看下os_atomic_dec2o()
方法具体做了什么:
#define os_atomic_dec2o(p, f, m) \ os_atomic_sub2o(p, f, 1, m) =======> 宏定义
#define os_atomic_sub2o(p, f, v, m) \ os_atomic_sub(&(p)->f, (v), m) ========> 宏定义
#define os_atomic_sub(p, v, m) \ _os_atomic_c11_op((p), (v), m, sub, -) ========> 宏定义
连续的几个宏定义,其实后面还有,不过看到这里已经够了,sub
、1
、-
,不知道大家有没有看到这些敏感的字样,实际上这一步就是进行-1
操作。
再看_dispatch_semaphore_wait_slow()
方法,走到这步,说明value
值是小于0的
_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema,
dispatch_time_t timeout)
{
long orig;
_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
switch (timeout) {
default:
if (!_dispatch_sema4_timedwait(&dsema->dsema_sema, timeout)) {
break;
}
// Fall through and try to undo what the fast path did to
// dsema->dsema_value
case DISPATCH_TIME_NOW:
orig = dsema->dsema_value;
while (orig < 0) {
if (os_atomic_cmpxchgvw2o(dsema, dsema_value, orig, orig + 1,
&orig, relaxed)) {
return _DSEMA4_TIMEOUT();
}
}
// Another thread called semaphore_signal().
// Fall through and drain the wakeup.
case DISPATCH_TIME_FOREVER:
_dispatch_sema4_wait(&dsema->dsema_sema);
break;
}
return 0;
}
咔咔咔。。。其实这么多代码,就是告诉我们会一直的进行等待,它是对我们第二个参数设置的遍历。
我们这里设置的是forever
,永久等待。注释也帮我们解答了,如果想要唤醒,那么就调用semaphore_signal()
方法。
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
long value = os_atomic_inc2o(dsema, dsema_value, release);
if (likely(value > 0)) {
return 0;
}
if (unlikely(value == LONG_MIN)) {
DISPATCH_CLIENT_CRASH(value,
"Unbalanced call to dispatch_semaphore_signal()");
}
return _dispatch_semaphore_signal_slow(dsema);
}
这里代码和前面的wait
很相似,先看看os_atomic_inc2o()
方法:
#define os_atomic_inc2o(p, f, m) \ os_atomic_add2o(p, f, 1, m)
#define os_atomic_add2o(p, f, v, m) \ os_atomic_add(&(p)->f, (v), m)
#define os_atomic_add(p, v, m) \ _os_atomic_c11_op((p), (v), m, add, +)
通过上面的代码,我相信聪明的你已经猜到了os_atomic_inc2o()
的意义了,没错,就是+1
操作。
后面的方法_dispatch_semaphore_signal_slow
表示持续加一的状态,最后返回1
,表示为非阻塞状态。
调度组dispatch_group_t
dispatch_group_create()
dispatch_group_enter()
dispatch_group_leave()
dispatch_group_async(<#dispatch_group_t _Nonnull group#>, <#dispatch_queue_t _Nonnull queue#>, ^{
});
dispatch_group_notify(, , );
调度组我们围绕这几个方法来说,先看创建dispatch_group_create()
dispatch_group_create(void)
{
return _dispatch_group_create_with_count(0);
}
_dispatch_group_create_with_count(uint32_t n)
{
dispatch_group_t dg = _dispatch_object_alloc(DISPATCH_VTABLE(group),
sizeof(struct dispatch_group_s));
dg->do_next = DISPATCH_OBJECT_LISTLESS;
dg->do_targetq = _dispatch_get_default_queue(false);
if (n) {
os_atomic_store2o(dg, dg_bits,
-n * DISPATCH_GROUP_VALUE_INTERVAL, relaxed);
os_atomic_store2o(dg, do_ref_cnt, 1, relaxed); //
}
return dg;
}
可以看出就是创建了一个dispatch_group_t
类型的对象,比较简单。
下面看dispatch_group_enter()
:
dispatch_group_enter(dispatch_group_t dg)
{
// The value is decremented on a 32bits wide atomic so that the carry
// for the 0 -> -1 transition is not propagated to the upper 32bits.
uint32_t old_bits = os_atomic_sub_orig2o(dg, dg_bits,
DISPATCH_GROUP_VALUE_INTERVAL, acquire);
uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
if (unlikely(old_value == 0)) {
_dispatch_retain(dg); //
}
if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {
DISPATCH_CLIENT_CRASH(old_bits,
"Too many nested calls to dispatch_group_enter()");
}
}
其实可以通过注释看到下面的操作是0 -> 1的操作,很明显是这个方法os_atomic_sub_orig2o
#define os_atomic_sub_orig2o(p, f, v, m) \ os_atomic_sub_orig(&(p)->f, (v), m)
#define os_atomic_sub_orig(p, v, m) \ _os_atomic_c11_op_orig((p), (v), m, sub, -)
然后判断,等于0直接返回,造成堵塞。
接着看dispatch_group_leave()
:
dispatch_group_leave(dispatch_group_t dg)
{
// The value is incremented on a 64bits wide atomic so that the carry for
// the -1 -> 0 transition increments the generation atomically.
uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state,
DISPATCH_GROUP_VALUE_INTERVAL, release);
uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK);
if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) {
// 省略一些无关代码
return _dispatch_group_wake(dg, old_state, true);
}
if (unlikely(old_value == 0)) {
DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
"Unbalanced call to dispatch_group_leave()");
}
}
同样的道理,第一步就是执行+1
的操作,这里就不重复了。最后面有个判断,表示+1
操作之后,还等于0,就会报错,这就要求dispatch_group_enter()
和dispatch_group_leave()
要成对出现,不然会有问题。
主要看后面的方法_dispatch_group_wake
_dispatch_group_wake(dispatch_group_t dg, uint64_t dg_state, bool needs_release)
{
uint16_t refs = needs_release ? 1 : 0; //
if (dg_state & DISPATCH_GROUP_HAS_NOTIFS) {
dispatch_continuation_t dc, next_dc, tail;
// Snapshot before anything is notified/woken
dc = os_mpsc_capture_snapshot(os_mpsc(dg, dg_notify), &tail);
do {
dispatch_queue_t dsn_queue = (dispatch_queue_t)dc->dc_data;
next_dc = os_mpsc_pop_snapshot_head(dc, tail, do_next);
_dispatch_continuation_async(dsn_queue, dc,
_dispatch_qos_from_pp(dc->dc_priority), dc->dc_flags);
_dispatch_release(dsn_queue);
} while ((dc = next_dc));
refs++;
}
if (dg_state & DISPATCH_GROUP_HAS_WAITERS) {
_dispatch_wake_by_address(&dg->dg_gen);
}
if (refs) _dispatch_release_n(dg, refs);
}
走到这步就表明出里调度组,执行队列里的所有任务,里面有个do...while
循环。
下面看另一个方法dispatch_group_async()
dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_block_t db)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_GROUP_ASYNC;
dispatch_qos_t qos;
qos = _dispatch_continuation_init(dc, dq, db, 0, dc_flags);
_dispatch_continuation_group_async(dg, dq, dc, qos);
}
第一步_dispatch_continuation_init()
操作是保存任务块
接着看_dispatch_continuation_group_async()
_dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_continuation_t dc, dispatch_qos_t qos)
{
dispatch_group_enter(dg);
dc->dc_data = dg;
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
走到这里我们看到一个熟悉的方法dispatch_group_enter()
上面刚刚说过的,而方法_dispatch_continuation_async()
在上一篇文章里描述函数的部分也介绍过。不了解的小伙伴可以去看看
接着看方法_dispatch_group_notify()
:
_dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_continuation_t dsn)
{
prev = os_mpsc_push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
if (os_mpsc_push_was_empty(prev)) _dispatch_retain(dg);
os_mpsc_push_update_prev(os_mpsc(dg, dg_notify), prev, dsn, do_next);
if (os_mpsc_push_was_empty(prev)) {
os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, release, {
new_state = old_state | DISPATCH_GROUP_HAS_NOTIFS;
if ((uint32_t)old_state == 0) {
os_atomic_rmw_loop_give_up({
return _dispatch_group_wake(dg, new_state, false);
});
}
});
}
}
其实前面的一系列判断说什么都不重要,最重要的是我们看到了方法_dispatch_group_wake()
,是不是又回到了dispatch_group_leave()
方法
总结:
其实dispatch_group_async()
与_dispatch_group_notify()
就是对dispatch_group_enter()
和dispatch_group_leave()
的封装,不过个人感觉后面两者更加灵活。
上面描述如果有什么错误,还希望小伙伴们指出,一起学习一起交流!!!