我们都知道,程序启动就会创建一个主线程来执行程序,我们先看一下默认开启的主线程的相关信息。在main
函数打一个断点,看函数调用栈:
可以看到当前线程:Queue: com.apple.main-thread(serial)
,它的名字是com.apple.main-thread
,它的类型是serial
也就是串行队列。
这个主线程是什么时候创建又是怎么调用的呢?我们可以看到在main
函数之前,主线程就已经被创建好,猜测应该是dyld
链接之后,main
函数之前创建的。那我们怎么证明呢?这就要去libdispatch
源码中找线索了。
在源码中找类似init
操作看有没有主线程相关的初始化。全局搜索dispatch_get_main_queue(
,找到下面实现:
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_CONST DISPATCH_NOTHROW
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
#define DISPATCH_GLOBAL_OBJECT(type, object) ((type)&(object))
这个宏定义第一个参数type
是类型,值是object
。也就是说dispatch_queue_main_t
是类型,_dispatch_main_q
这个才是真正的值。全局进行搜索,发现结果有很多,这个变量要调用一定会有赋值的地方,尝试搜索_dispatch_main_q =
,就会发现只有一个地方赋值:
struct dispatch_queue_static_s _dispatch_main_q = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
.do_targetq = _dispatch_get_default_queue(true),
#endif
.dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
DISPATCH_QUEUE_ROLE_BASE_ANON,
.dq_label = "com.apple.main-thread",
.dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
.dq_serialnum = 1,
};
从上面代码可以看到,主队列的名字com.apple.main-thread
,跟我们上面看函数调用栈的结果一样,也就是没找错地方。主队列的类型DQF_WIDTH(1)
,串行队列,很多人说.dq_serialnum = 1
看到这个num = 1
就是主队列,其实不是,这只是队列的编号,怎么证明呢?串行队列必然相对并发队列有某些特性,我们找的就是这些特性来证明。
我们创建队列,都是用dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)
这个函数来创建,现在来研究下创建的底层实现。
全局搜索dispatch_queue_create(con
,为什么要加(con
因为我们要找他的底层实现,第一个参数是const
类型,这样搜索结果更少方便定位。
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_lane_create_with_target
的实现,有100多行代码,我们研究的重点是看它创建的队列是什么,也就是返回的是什么队列,所以我们先关注返回值。
返回值是_dispatch_trace_queue_create(dq)._dq
,里面trace
的含义就是方便追踪,不是关注重点,重点是里面的dq
,我们看dq
是如何创建的。从_dispatch_lane_create_with_target
方法中找相关代码:
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
第一行是开辟内存空间,第二行是构造函数。看构造函数第三个参数,dqai.dqai_concurrent ? DISPATCH_QUEUE_WIDTH_MAX : 1
,命名意思比较明显,判断是不是并发队列,如果是传max
,如果不是并发队列(即串行队列),参数就传1。
我们再看构造函数的实现,看这个参数是如何使用的:
可以看到参数使用dqf |= DQF_WIDTH(width);
,也就是为什么我们上面说DQF_WIDTH(1)
是队列的类型。
再看下dq_serialnum
,它这个值是根据os_atomic_inc_orig
创建
#define os_atomic_inc_orig(p, m) \
os_atomic_add_orig((p), 1, m)
#define os_atomic_add_orig(p, v, m) \
_os_atomic_c11_op_orig((p), (v), m, add, +)
#define _os_atomic_c11_op_orig(p, v, m, o, op) \
atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), v, \
memory_order_##m)
这个函数最后就会变成atomic_fetch_add_explicit(_os_atomic_c11_atomic(p), 1, memory_order_relaxed)
,这个宏一层层封装最后就是C11标准的函数,atomic_fetch_add_explicit
可以理解是把前两个参数想加并返回,_os_atomic_c11_atomic
表示是原子操作。
那看下第一个参数的值_dispatch_queue_serial_numbers
是什么:
// skip zero
// 1 - main_q
// 2 - mgr_q
// 3 - mgr_root_q
// 4,5,6,7,8,9,10,11,12,13,14,15 - global queues
// 17 - workloop_fallback_q
// we use 'xadd' on Intel, so the initial value == next assigned
#define DISPATCH_QUEUE_SERIAL_NUMBER_INIT 17
extern unsigned long volatile _dispatch_queue_serial_numbers;
可以看到1是主队列的意思,4-15全局队列,我们用dispatch_get_global_queue
创建的队列为什么没有2、3在注释中可以得到解释。
这里有个想法,这个队列和线程的num
是不是一致的呢?我们创建几个线程并打印:
明显看到线程num
有等于3(不一定一次运行就出现,我也是运行了好多次),也就是线程的num
和队列的num
并不是一个东西,不要混淆了。
我们分别获取4种不同的线程打印下线程信息:
去源码中搜索com.apple.root.default-qos
,可以看到全局队列对应的num
是10
_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
.dq_label = "com.apple.root.default-qos",
.dq_serialnum = 10,
),
我们现在再回到_dispatch_lane_create_with_target
这个方法中,看第一行代码
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
这行代码的意义就是对线程一些信息进行面向对象的封装。再全局搜索dispatch_queue_attr_info_t
看下他的结构:
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;
一个用位域表示的结构体。
看下_dispatch_queue_attr_to_info
实现,里面有个小细节,如果dqa
是空的话,就直接返回。
dispatch_queue_create
创建队列的时候,如果attr
传空,通过dqai.dqai_concurrent
判断就会是串行队列,如果attr
传DISPATCH_QUEUE_SERIAL
,可以看到这个宏的实现#define DISPATCH_QUEUE_SERIAL NULL
也是空,所以他们是等价的,都是创建串行队列。
队列的类型
无论我们直接获取主队列或者全局队列又或者自己创建的队列,最后我们接收的类型都是dispatch_queue_t
,点进去看这个类型:DISPATCH_DECL(dispatch_queue);
,这里我们可以看到有好几个宏定义,他们判断不同,第一个#if OS_OBJECT_USE_OBJC
,正常会走到这个if里面。
再继续点就可以看到
#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)
继续往下看发现没有了,我们只能从源码中搜索OS_OBJECT_DECL_SUBCLASS
,找到三个宏定义:
#define OS_OBJECT_DECL_SUBCLASS(name, super) DISPATCH_DECL(name)
这个相当于跟上面DISPATCH_DECL
宏定义死循环了,不是这个。
#define OS_OBJECT_DECL_SUBCLASS_SWIFT(name, super) \
OS_EXPORT OS_OBJECT_OBJC_RUNTIME_VISIBLE \
OS_OBJECT_DECL_IMPL_CLASS(name, OS_OBJECT_CLASS(super))
这个的条件是#if OS_OBJECT_SWIFT3
,也不是这个。
就剩下一个:
#define OS_OBJECT_DECL_SUBCLASS(name, super) \
OS_OBJECT_DECL_IMPL(name, )
继续往下探索这个宏定义实现:
#define OS_OBJECT_DECL_IMPL(name, ...) \
OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
typedef NSObject \
* OS_OBJC_INDEPENDENT_CLASS name##_t
#define OS_OBJECT_DECL_PROTOCOL(name, ...) \
@protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \
@end
#define OS_OBJECT_CLASS(name) OS_##name
DISPATCH_DECL(dispatch_queue);
OS_OBJECT_DECL_SUBCLASS(dispatch_queue, dispatch_object)
OS_OBJECT_DECL_IMPL(dispatch_queue,
) -
OS_OBJECT_DECL_PROTOCOL(dispatch_queue,
)
typedef NSObject
* OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t
-
@protocol OS_OBJECT_CLASS(dispatch_queue)
@end
typedef NSObject
* OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t
-
@protocol os_dispatch_queue
@end
typedef NSObject
* OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t
也就是dispatch_queue_t
本质是一个满足os_dispatch_queue
协议的对象。
第二个#elif defined(__cplusplus) && !defined(__DISPATCH_BUILDING_DISPATCH__)
,判断是C++第二个条件是YES,也就是说底层C++实现可能会走到这个if
#define DISPATCH_DECL(name) \
typedef struct name##_s : public dispatch_object_s {} *name##_t
typedef struct dispatch_queue_s : public dispatch_object_s {} *dispatch_queue_t
也就是这种情况dispatch_queue_s
类型继承dispatch_object_s
的结构体。
在源码中我们还能看到一个联合体:
typedef union {
struct _os_object_s *_os_obj;
struct dispatch_object_s *_do;
struct dispatch_queue_s *_dq;
struct dispatch_queue_attr_s *_dqa;
struct dispatch_group_s *_dg;
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_object_t
这个可以是联合体里面的各种类型。
之后的条件就没必要看了,基本不会走。
它的底层结构因为底层是C++,所以我们看第二个,看下dispatch_queue_s
结构体的实现:
struct dispatch_queue_s {
DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
/* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;
#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; \
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
#define _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
DISPATCH_OBJECT_HEADER(x); \
__pointer_sized_field__; \
DISPATCH_UNION_LE(uint64_t volatile dq_state, \
dispatch_lock dq_state_lock, \
uint32_t dq_state_bits \
)
#endif
#define DISPATCH_OBJECT_HEADER(x) \
struct dispatch_object_s _as_do[0]; \
_DISPATCH_OBJECT_HEADER(x)
#define _DISPATCH_OBJECT_HEADER(x) \
struct _os_object_s _as_os_obj[0]; \
OS_OBJECT_STRUCT_HEADER(dispatch_##x); \
struct dispatch_##x##_s *volatile do_next; \
struct dispatch_queue_s *do_targetq; \
void *do_ctxt; \
void *do_finalizer
#define OS_OBJECT_STRUCT_HEADER(x) \
_OS_OBJECT_HEADER(\
const struct x##_vtable_s *do_vtable, \
do_ref_cnt, \
do_xref_cnt)
#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \
isa; /* must be pointer-sized */ \
int volatile ref_cnt; \
int volatile xref_cnt
一层一层宏包装的继承链。
GCD任务块执行时机
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
});
上面代码在全局并发队列同步执行一个打印,那这个block
块里面的代码是什么时候实现的呢?从源码中找线索:
void
dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
uintptr_t dc_flags = DC_FLAG_BLOCK;
if (unlikely(_dispatch_block_has_private_data(work))) {
return _dispatch_sync_block_with_privdata(dq, work, dc_flags);
}
_dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}
这里重点关注work
即可,就是我们执行的block
。
搜索_dispatch_Block_invoke
可以看到是个宏定义:
#define _dispatch_Block_invoke(bb) \
((dispatch_function_t)((struct Block_layout *)bb)->invoke)
执行invoke
也就是这个就是work
的调用,继续看_dispatch_sync_f
是从哪调用过来的,
DISPATCH_NOINLINE
static void
_dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
uintptr_t dc_flags)
{
_dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
}
ctxt
就是work
,func
就是block
函数。
我们看到_dispatch_sync_f_inline
函数有好几个return
,到底走哪个也不好判断,这时候我们就把return
对应函数加到符号断点里面,看到底走哪个。
看到断点走到了_dispatch_sync_f_slow
位置,在源码中搜索_dispatch_sync_f_slow
的实现,里面也是有几个return
,我们故技重施,继续添加符号断点并且重新运行,
看到后面走到的_dispatch_sync_function_invoke
这个方法,
static void
_dispatch_sync_function_invoke(dispatch_queue_class_t dq, void *ctxt,
dispatch_function_t func)
{
_dispatch_sync_function_invoke_inline(dq, ctxt, func);
}
static inline void
_dispatch_sync_function_invoke_inline(dispatch_queue_class_t dq, void *ctxt,
dispatch_function_t func)
{
dispatch_thread_frame_s dtf;
_dispatch_thread_frame_push(&dtf, dq);
_dispatch_client_callout(ctxt, func);
_dispatch_perfmon_workitem_inc();
_dispatch_thread_frame_pop(&dtf);
}
参数ctxt
就是之前的work
,func
就是之前block
包装的函数,跟这个有关的就是_dispatch_client_callout(ctxt, func);
,全局搜索_dispatch_client_callout(void
,有几个地方:
static inline void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
return f(ctxt);
}
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
_dispatch_get_tsd_base();
void *u = _dispatch_get_unwind_tsd();
if (likely(!u)) return f(ctxt);
_dispatch_set_unwind_tsd(NULL);
f(ctxt);
_dispatch_free_unwind_tsd();
_dispatch_set_unwind_tsd(u);
}
这两个无论调用哪个,核心思想都是调用f(ctxt)
,也就是block
中的函数。
总结下dispatch_sync
中block
调用:
dispatch_sync
_dispatch_sync_f
_dispatch_sync_f_inline
_dispatch_sync_f_slow
_dispatch_sync_function_invoke
_dispatch_sync_function_invoke_inline
_dispatch_client_callout
f(ctxt)
在block
中打个断点,看下函数调用栈:
跟我们分析一致。
接下来我们看下dispatch_async
中block
的调用时机。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
});
全局搜索dispatch_async(dis
void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME;
dispatch_qos_t qos;
qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
我们还是关注work
,找_dispatch_continuation_init
的实现
unlikely
不用关注,再找_dispatch_continuation_init_f
实现
也就是说,qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
这行代码的意义就是把block
进行对应的封装以及优先级处理。因为是异步的,所以需要优先级来进行函数执行的参考和依据。
函数想要执行,肯定就要依赖下面的代码_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
因为函数最后包装在qos
里面,所以我们关注z
这个参数,全局搜索dq_push
全局并发队列是在这赋值,找_dispatch_root_queue_push
实现:
里面第三个参数是函数封装,并没有找到相关的调用,看最后方法,继续找:
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_root_queue_push_inline(dispatch_queue_global_t dq,
dispatch_object_t _head, dispatch_object_t _tail, int n)
{
struct dispatch_object_s *hd = _head._do, *tl = _tail._do;
if (unlikely(os_mpsc_push_list(os_mpsc(dq, dq_items), hd, tl, do_next))) {
return _dispatch_root_queue_poke(dq, n, 0);
}
}
static inline void
_dispatch_root_queues_init(void)
{
dispatch_once_f(&_dispatch_root_queues_pred, NULL,
_dispatch_root_queues_init_once);
}
这里面封装单利,执行一次_dispatch_root_queues_init_once
,单利的原理我们在下面会分析。
我们先看下函数调用栈:
函数调用是从_dispatch_worker_thread2
这个函数调用过来,在方法里面找这个函数相关内容:
这个就是底层
pthread
的相关封装,它是通过workloop
来控制是否调用,这个workloop
又是通过OS
控制,受CPU的调度处理。
死锁分析
dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1");
dispatch_sync(queue, ^{
NSLog(@"2");
});
NSLog(@"3");
});
我们看上面的一段代码,运行就会发现崩溃,可以看到崩溃的函数调用栈:
死锁崩溃我们可以看到先走到_dispatch_sync_f_slow
函数,然后再走__DISPATCH_WAIT_FOR_QUEUE__
发生崩溃,那现在从源码分析一下什么情况下回产生死锁。
我们上面已经分析到,dispatch_sync
的流程,一开始流程没有区别:
dispatch_sync
_dispatch_sync_f
_dispatch_sync_f_inline
走到这都一样,但是接下来,就不太一样了,看likely(dq->dq_width == 1)
这个条件,上面分析过dq_width
为1时为串行队列,死锁就是在串行队列才会有,所以我们看这个if
里面的代码:return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
static void
_dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func, uintptr_t dc_flags)
{
_dispatch_barrier_sync_f_inline(dq, ctxt, func, dc_flags);
}
因为我们上面已经看到,死锁会走到_dispatch_sync_f_slow
函数,所以看这个方法我们只需要找到这个函数的调用,又回到之前调用的流程中函数,但是这次判断就会不一样了:
为了验证我们分析是正确的,首先运行看函数调用:
再看源码中__DISPATCH_WAIT_FOR_QUEUE__
方法:
#define DISPATCH_CLIENT_CRASH(c, x) do { \
_dispatch_set_crash_log_cause_and_message((c), \
"BUG IN CLIENT OF LIBDISPATCH: " x); \
_dispatch_hardware_crash(); \
} while (0)
就可以看到源码分析中输出的message
和我们运行汇编message
一模一样,所以分析正确!其实在messag
里面已经说明了什么情况会死锁,dispatch_sync
调用的队列,已经是当前的线程中了。
流程没问题了,虽然message
我们可以看出端倪,但是我们还是想知道代码如何判断会产生死锁,也就是说,什么条件下会走到这些代码呢。最重要的还是要看__DISPATCH_WAIT_FOR_QUEUE__
方法里面的判断
if (unlikely(_dq_state_drain_locked_by(dq_state, dsc->dsc_waiter))) {
DISPATCH_CLIENT_CRASH((uintptr_t)dq_state,
"dispatch_sync called on queue "
"already owned by current thread");
}
当条件满足时,才会走到下面的方法,我们现在就分析这个条件。
dsc->dsc_waiter
就是当前线程的id
,dsc
是上面参数传过来的,
#define _dispatch_tid_self() ((dispatch_tid)(_dispatch_get_tsd_base()->tid))
dq_state = _dispatch_wait_prepare(dq);
代表当前队列的状态。
_dq_state_drain_locked_by
这个函数实现:
static inline bool
_dq_state_drain_locked_by(uint64_t dq_state, dispatch_tid tid)
{
return _dispatch_lock_is_locked_by((dispatch_lock)dq_state, tid);
}
static inline bool
_dispatch_lock_is_locked_by(dispatch_lock lock_value, dispatch_tid tid)
{
// equivalent to _dispatch_lock_owner(lock_value) == tid
return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}
这里面的掩码DLOCK_OWNER_MASK
是很大的值#define DLOCK_OWNER_MASK ((dispatch_lock)0xfffffffc)
,也就是前面如果是0的话,返回YES
就会造成死锁,也就是state
和tid
相同时,会死锁,也就是说当前线程要等待,然后你又调用当前线程执行,就会造成死锁。
单利原理分析
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"once");
});
gcd
单利写法如上面代码,底层是怎么实现的呢,我们现在来研究一下。
在源码中全局搜索dispatch_once
找到它的实现:
void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
typedef struct dispatch_gate_s {
dispatch_lock dgl_lock;
} dispatch_gate_s, *dispatch_gate_t;
typedef struct dispatch_once_gate_s {
union {
dispatch_gate_s dgo_gate;
uintptr_t dgo_once;
};
} dispatch_once_gate_s, *dispatch_once_gate_t;
判断能否进入函数执行:
static inline bool
_dispatch_once_gate_tryenter(dispatch_once_gate_t l)
{
return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
(uintptr_t)_dispatch_lock_value_for_self(), relaxed);
}
调用函数并广播:
static void
_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_once_gate_t
,标记这个是否已经做过和一些锁 - 判断
dgo_once
等于DLOCK_ONCE_DONE
,相当于做过,直接return
- 如果尝试加锁失败,直接把
dgo_once
设置成DLOCK_ONCE_DONE
- 判断
l
没有被加锁,也就是没有别的线程操作,调用函数并且广播 - 等待其他人开锁