欢迎阅览iOS探究系列(按序阅览食用作用更加)
- iOS探究 alloc流程
- iOS探究 内存对齐&malloc源码
- iOS探究 isa初始化&指向剖析
- iOS探究 类的结构剖析
- iOS探究 cache_t剖析
- iOS探究 办n f j 5 F L E法的本质和办法查找流程
- iOS探究 动态办法解析和音讯转发机制
- iOS探究 浅尝辄止dyld加载流程
- iOS探究 类的加载进程
- iOS探究 分类、类拓展的加载进程
- iOS探究 isa面试题剖析
- iOS探究 runtime面试题剖析
- iOS探究 KVC原理及自界说
- iOS探究 KVO原理及自界说
- iOS探究 多线程原理
- iOS探究 多线程之GCD运用
- iOS探究 多线程之GCD底层剖析
- iOS探究 多线程面试题剖析
写在前面
因为源码的篇幅较大、逻辑分支、宏界说较多,使得源码变得不流畅难明,让开发者们望而却步。但假如带着疑问、有目的性的去看源码,就能削减难度,忽略无关的代码。首要提出本文剖析的几个问题:
- 底层行列是怎样创g o E Q立的
- 死锁的发生
- dispatch_block使命的履行
- 同步函数
- 异步函数
- 信号量的原理
- 调度组的原理
- 单例的原理
本文篇幅会比较大,函数之间的跳转也比较多,但只对中心流程代码做了研讨,信任看下来应该会有所收获
源n A p s 6码的选择判别
剖析源码+ V j 0 ] N M首要得获取到GCD
源码,之前现已0 v 2 2 t F剖析过objc、malloc、dyld源码,那么GCD
内| A d : Q @容是在哪份源码中呢?
这儿分享一个小技巧,因为已知要研讨GCD
,所以有以下几种选择源码的办法
- Baidu/Google
- 下符号断点
dispatch_queue_create
- 仅运用
Debug->Debug Workflow->Always show Disassembly
查看汇编也能看到
这样子就找到了咱们需求的libdispatcT = y g 2 Zh源码
一、底层行列是怎样创立的
上层运用dispatch_queue_create
,大局进行查找。可是会出现查找成果很多的状况(66 results in 17 files. { b W 4 + e N)g N R . P @ h q X,这时分就考验一个开发者阅览源码的经验了
- 新手会一个个找过去,宁可错杀一千不可放过一个
- 老司机则会根据上层运用修正查找条件
- 因为创立行列代码为
dispatch_queue_create("", NULL)
,所以查找dispatch_queue_create(
——将挑选成果降至(21 results in 6 files) - 因为第一个参数为字符串,在c言语中H { R H用
const
修饰,所以查找dispatch_queue_create(const
——将挑选成果降至(2 results in 2 files)
- 因为创立行列代码为
1.dispatch_queue_cre+ f p . m * 1 1 sate
常规中间层封装——便于代码迭代不改变上层运用
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
retS o p Z I u Uurn _dispatch_lane_create_with_target(label, attr,
DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
有时分也需求注意下源码中函数中的传参:
- 此刻
label
是上层的逆D H = h序全程域名
,主要用在溃散调试 -
attr
是NULL/DISPATCH_QUEUE_SERIX 0 ZAL
、DISPATCH_QUEUE_CONCURRENT
,用于区别行列是异步仍是同步的
#defiC d [ WneS C $ E DISPATCH_QUEUE_SERIAL NULL 串行行列的宏界说其实是个NULL
2._dispatch_lane_create_with_target
DISPATCH_NOINLINE
static dispatch_f 5 r 5 = @queue_t
_dispatch_lane_create_wit5 r _ :h_target(const char *label, dispatcd } + b Y 0 u :h_queue_attr_t dqa,
dispatch_queue_t tq, bool leg; h D ; -acy)
{
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
...
}
dqa
是这个函数中的第二个参数,即dispatcs I ; 2 Zh_queue_create
中的aU m c Yttr
用到了串行/并发的区别符,咱们就跟进去瞧瞧
3._dispatch_queue_attr_toc a { U m_info
diT : O ; ; Jspatch_queue_atY i C S V % d Mtr_info_t
_dispatq X I u b / gch_queue_attr_to_infb a [ x Wo(dispatch_queue_attr_t dqa)
{
dispatch_queue_attr_info_t dqai = { };
if (!dqa) return dqai;
#if DISPATCH_VARIANT_STATIC
if (dqa ==6 , 2 T j &_dispatch_queue_attr_concurrent) {
dqai.dc E c h l [ x 8qai_concurrent = true;
return dqai;
}
#endif
if (dqa < _dispatch_; 7 Tqueue_attrs ||
dqa >= &_dis] / l g @patch_queue_at* b T f N # [ 6 (trs[DISPATCH_QUEUE_ATTR_COUNT]) {
DISPATCH_CLIENT_CRASH(dqa-&gv p a zt;do_vtable, "Invalid queue an 3 o n P P s 7 &ttribute");
}
size_t idx = (size_t)(dqa - _dispatch_queue_attrs);
dqai] U P.dqai_inacs * 1 m P 3 y qtive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
idx /= DISPATCD D ^ 8 2H_QUEUE_ATTR_INACTIVU U 4 8 ] KE_COUNT;
dqai.dqai_concur` b H T Rrent = !(idx % DA T A [ISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;
dqai.dqai_relpri = -(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;
dqai.dqai_qos = idx % DISV r W i 1 d bPATCH_QUEUE_ATTC S , 5 % $ @ I 7R_QOS_COUNT;
idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;
dqai.dqai_autorelease_frequency =
idx % DISPATCH_QUEUE_ATTR_AUTR { ) X QORELEASE_FREQUENCY_COUNT;
idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
dqai.dqai_overcommit = i= 3 T b 8 8 #dx % DISPATCH_QUR @ V g g b a -EUE_ATTR_OVERCOMMIT_COUNT;
if J |dx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
return dqai;
}
-
dispatch_queue_attr_info_t dqai = { };
进行初始化-
dispatch_queue_attr_info_t
与isa相同,是个位域结构
-
typedef struct dispatch_queue_attr_info_s {
dispatch_q) g Z + d } h Ios_t dqai_qos : 8;
int dqai_relpri : 8;
uint16_t dqai_overcommit:2;
uint16+ $ q_t dqai_autorelease_frequency:2;
uint16_t dqai_concn 2 yurrent:1;
uint16_t dqai_inactive:1;_ ? B q y 4 ? l
} dispatch_quea = W y & ? 8 9ue_attr_info_t& j $ W O B;
- 接下来把目光放到这句代码
if (!dqa) return dqai;
- 串行行列的
dqa
为NULL,! 0 1 Z P 2 / *直接回来NULLX H { * 8 | i I - 异步行列往下持续走
- 串行行列的
-
size_t idx = (size_t)(dqa - _dispatch_queue_attrs);
- 运用? 5 _
DISPATCH_QUEUE_CONCURRENT
的宏界说来进行位运算 - 并发行列的并发数
dqai.dqai_concurre| l u F q Lnt
与串行行列不同
- 运用? 5 _
#define DISPATCH_QUEUE_CONCURRENT
DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t,
_dispatch_queue_attr_concurrent)
4.回到_dispatch_lane_create_with_target
咱们要研讨的是行列的创立
,所以能够忽略源# + c B A c 8码中的细节,专心查q ; e + I 8 Z y T找alloc
之类的代码
DISPATCH_NOINLINE
staD u Otic dispatch_queue_t
_dispatch_lane_cs W c - lreate_with_target(const char *label, dispatch_queue_a& P j T n httr_t dqa,
dispatk S ] ech_queue_t tq+ P - * : ;, bool legacy)
{
dispatch_queue_y n M 2 _ $ Zattr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
dispatch_qos_t qos = dqai.dqai_qos;
...
// 这是部分的逻辑分支
if (tq->dd w a ! nq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
overcommit = _dispatch_queue_a@ 2 ? # i 0 S A cttr_overcommit_o m G E I o : E *enabledV = 4 . T h k;
} else {
oved _ # M n Y G ) Ercommit = _disx y M t 4 zpatch_queue_attr_on 8 6vercommit_disabled;
}
if (!tq) {
tq = _dispatch_get_root_queue(
qos == DISPATCH_QOS_UNSPECIFIED~ j - { P P ~ - ? DISPAX 3 W n / %TCH_QOS_DEFAULTU : w Z S c = c : qos, // 4
overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
if (unlikely(!tq)) {
DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
}
}
...
// 拓荒内存 - 生成响应的y 5 ( i ^目标 queue
dispat1 K ) Q ch_lane_t dq = _dispatch_of ^ o ] O k | * hbject_alloc(vtable,
sizeof(struct dispatch_lane_s));
// 构造办? 0 7 1法
_dispatq 0 + % 8ch_qr ^ :ueue_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 5 1 $ C r ~ q;
/b 7 m K ] X i F x/ 优先级
dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
dqai.dqai_reE ! Q H Z ^ dlpri);
if (overcom 9 ! L z 1 v I zmmit == _dispatch_queue_attr_overcommit_enabled) {
dq->dq_priority |= DISPATCH_PRIORITYQ A @ K ! S & l f_FLAG_OVERCOMMIT;
}
if (!dqai.dqai_iU 1 Y Mnactive) {
_dispatch_queue_priority_D q M `inherit_from_tl H # j ` B Karget(dq, tq);
_dispatch_lane_inherit_wlh_from_target(dq, tq);
}
_dispatch_retain(tq);
dq->do_targetq = tq;
_dispatch_object_debug(dq, "%s", __func__);
return _dispatch_trace_queue_create(dq)._dq;
}
4.1 _dispatch_get_root_queue创立
-
tq
是当时函数_dispatch_lane_create_with_target
中的参数,该函数被调用时传了DISPATCH_TARGET_QUEUE_DEFAULT
,所以if (!tq)
一定为真
#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
-
_dispatch_get_root_queue
两个传参分别为4
和0/1
-
qos == DISPATCH_QOS_UNSPECIFIEDp i 2 E e = 0 ? DISZ 6 M S S J T ]PATCH_QOS_DEFAULT : qos
因为没有对qos
进行过赋值,所以默以为0 -
overcommit == _dis` V ,patq V b . O y {ch_queue_attr_overcommit_enabled)->_as_dq;
在代码区有补白——“这是部分的逻辑分支s & q h w u e”L z + : a d K,串行行列为_dispatch_queue_attr_overcommit_disabled
,并发行列是_dispatch_queue_attr_overcommit_enabled
-
#define DISPATCH_` b | % { X )QOS_UNU G ~SPECIFIED ((dispatch_qos_t)0)
#define D` m L : , + ;ISPATCH_QOS_DEFAULT (K % N Q { o * 9(dispatch_qos_t)4 i L =4)
Ds R Z e 1 U MISPATCH_ALWAYS_INLINE DISPAs ) l U x # i pTCH_CONST
static inline dispatch_queue_global_t
_dispatch_get_root_queo ] zue(dispatch_qo, 8 E m 0 f )s_t qos, bool overcommit)
{
if (unlikely(qos < DISPc ` h (ATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
}
return &_d l B i U N j %dispatch_root_queuesR G _ u { Y e #[2 * (qos - 1) + overcommit];J 3 y M ,
}
- 串行行列、并发行列分别renturn
&_dispatch7 O [ D H_root_queues[6]
、&amj 0 _ p 2p;_dispatch_rool x ! . | %t_queues[7]
大局查找_dispatch_root_queues[]
(因为是个数组5 m 9 @ r l q)能够看到数组中第7个和第8个分别是串行行列和并发行列
猜测:自界说行列是从_dispatch_root_queues
模板中拿出来创立的
4.2 _= N ^ q = v idispatch_object_alloc拓荒内存
其实GCD目U ` b @ f 4 r Z e标
多为dispatch_object_t
创立而来:苹果源码注释中有提到——它是所有分配目标的抽象基类型;dispatch_object_t
作为一个联合体,只要一创立,就能够运用你想要的类型(联合体的“有我没他”特点)
联合: } s体的思维属于多态,跟objc_object的继承思维略有不同
/*!o @ g
* @typedef dispatch_object_t
*
* @abstract
* Abstract base type for all dispatch objects.
* The details of the type definX _ ~ @itioK S ! ~ h 0 tn are lano ) : 7 R x : tguage-specific.~ % { & r P ]
*
* @discussiq ; N j s y 7 _on
* Dispatch objects are reference counted via calls to dispatch_retain() and
* dispatch_release().
*/
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 *_9 8 ~dg;
struct dispatch_sour2 j O A K 7 Ace_s *_ds;
struct dispat2 = + ` & ( V Och_mach_s *_dm;
struct dispatch_mach_m` B i S S ) j H |sg_s *_dmsg;
struct dispatch_semaphore_s *Z o h_dsema;
struct dispatcV 2 ^ 0 i ^ c ph_data_s *_ddata;
struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRAN= % s SPARENc u ~ ]T_UNION;
4.3 _dispatch_queue_init进行构造
- 前文中提到= 5 *了并发行列的
dqai.dqai_concurrent
进行l ^ l ? V R了设置,所以用dqai.dqai_concurrent
进行区别并发行列和串行行列 - 串行行列的
width
为17 a ! Q,并发行列的width
为DISPATCH_QUEUE_WIDTH_Mt % AAX
- 咔咔一顿操作之后T ~ j = o d Q回来
dispatch_queue_class_t
,即对传参dqu
进行了赋值修正等操作
4.4 回来dispatch_queue_t( r j ? 6 h 1
_dispatch_retain(tq);
dq->do_targetq = tq;
_dispatch_object_debug(dq,Z ( = J 4 F q v b "%s",6 g I S H g L __func__);
return _di5 J 7spatch_trace_queue_create(dq)._dq;z 1 / x : - n A
-
dq
是dispatch_lane_t
类型 -
tq
是dispatch_queue_t
类型 -
_dispat: l # | G Hch_trace_queue_create(dq)
回来类型为dispatch_queue3 n 0 0 a_class_t
,它是个联合体-
dispatch_f D = ` y /queue_class_t
中的dq. g + Q 2 5 7 ~
便是终究回来的dispatch_queue_t
-
typedef struct dispatch_queue_s *dispatch_queue_t;
typedef union {
struct dispatch_queue_s *_dq;
struct dispatch_workloop_s *_dwl;
struct dispatch_lap b b e # Ine_s *_dl;
struct disp^ 8 )atch_queue_static_s] K $ = G *_dsq;
struct diC E p Q T S uspatch_queue_global_s *_dgq;
struct dispatch_queue_pthread_root_= i U | o U $ R us *_dpq;
struct dispatch_source_s *} y ^ R_ds;
s, ` 2 3 ?truct dispatch_mach_s *_dm;
dispatch_lane_class_t _dlu;
#ifdef __OBJC__
id<OS_dispatch_queue> _objc_dq;
#endif
} dispatch_queue_class_t DISPATCH_TR0 m v f W 9 2ANr N ` E YSPARENT_U; ? W 5 / CNION;
5.验证猜N K S测
NSLog会调用目标的describtion办法,而LLDB能够打印底层的指针
- 能够看到串行行列、并发行列的
target
与模板中对应的一模相同 - 相同的,串行o } b 5 , i行列、并发行列、主行列、大局行列的
width
各不相同(width
表明行列中能调度z 4 e k 2 2 * d F的最大使命数)- 串行行列和主行列的
width
没有疑问 - 并发行列的
width
为DISP/ C | h & g # K oATCH_QUEUE_WIDTH_MAX
是满u } i e $ 9 J j值-2 - 大局行列的
width
为DISPATCH_QUEUE_WIDTH_POOL
是满值-1
- 串行行列和主行列的
#define DISPATCH_QUEUE_WIDTH_FULL_BIT 0x0020000000000000ull
#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULLK X g - 1)
#define DISPATCH_QUEUE_WIDTH_MAn F | AX (DISPATCZ Z s - Y - ] 5H_QUj ] MEUE_WIDTH_FULL - 2)
struct dispatch_queue_stato Z 3 ! 9 , { !ic_s _dispatch_main_q = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USEM l Z %_RES} = E 3 1OLVERS
.do_targetq = _dispatch_get_default_queue(true),
#en, ^ 3dif
.dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
DISPATCH_QZ L p 2 v 6 = ~UEUE_ROo q & /LE_BASE_ANON,
.dq o + % G_label = "com.apple.main-thread",
.dq_atomic_flags = DQF_THREAD_BOUND | DQF_WID7 j C 9THX 2 [ d e(1),
.dq_p 0 ] S : r V !serialnum = 1,
};
struct dispatch_queuh U f v e 1e_glo{ g G S z 8 bal_s _dispatch_mgr_root_queue = {
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global),
.dq_state = DISPATCH_RE n . S m c K AOOT_QUEUE_ST9 F h K ;ATE_INIT_VALUE,
.do_ctxt = &_dispatch_mgr_root_queue_pthread_context1 c 5 b r X H -,
.dq_label = "coG g vm.apple.root.libdispatch-manager",
.dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), } : ] * T a 5,
.dq_pr9 m ) `iority = DISPATCH_PRIORITY_FLAG_MANAGER |
DISPATCH_PRIORITY_SATURATED_OVERRIDE,
.dq_serialnum = 3,
.dgq_tf R D Bhread_pool_size = 1,
};
处理了自界说行列的创立9 ) S z S流程,那I + i / r 5 0 # +么问题又来了,
_dispatch_root_queues的创立
又是怎样创立的呢?
6._dispatch_root_queues的创立
除了dispatch_get_main_queue
,其他行列都1 c ` d * S . d ^是通过_dispatcE = 1 *h_root_queues
创立的
libdispatch_w 4 d # p E }init
之后调用_dispatcE I N =h_in( s U E R q { R itrospection_init
,通过 for 循环,调用_dispatch_trace_queue_create% e A
,再取出_dispatch_root_queues
里的地址指针一个个创立出来的
7.一图看懂自界说行列创立流程
二、死锁的发生& F r [ B y T 1 S
死锁的发生是因为使命的彼此等候,那么底层又是怎样实现死锁的呢?
1.dispatch_sync
大局查找dispatch_sync(dispatch
,忽略unlikely
小概率事件
DISPATCH_NOINLINE
void
dispatch_sync(dix C # v = ospatch_queue_t dq, dispatch_block_t! m ? work)
{
uintptr_t dc_flags = DC_FLAG_BLOCK;
if (unlikely(_dispatch_block_has_private_data(work))) {@ e e h 6 l { +
return3 S d 7 O n ; n 5 _dispatch_sync_block_with_privdata(dq, workp X f K, dc_flags);
}
_dispatch_sync_f(dq, work,w z S h . W X _dispB t Match_Block_invoke(work), dc_flags);
}
#endif // __BLOCKS__g A _ ( R a
2._dispatch_sy. t B B c Jnc_f
仍是常规的中间层封装
DISPA{ s A JTCH_NOINLINE
static void
_dispat. @ [ l ( 3 ?ch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
uintptr_t dc_flags)
{
_disp: G F Q * : * tatch_sync_f_inline(dq, ctxt, func, dc_flags);
}4 l k
3._dispatch_sH . wync_f_inline
- 已知
串行行列
的width
是1,所以串行行列满足dq. + % Q->dq_width == 1
return _dispatchR 1 B f _barrier_sync_f(dq, ctxt` l X ,, func, dc_flags);
-
并发行列
会持续往下走
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
dispatch_functX @ 0 Z ` a | ;ion_t func, uintptr_t dc_flags)
{
if (likely(dq->dq_width == 1)) {
return _dispatch_barrier_sync_f(dq, ctxt, func, dc_fl0 L / Y d [ } $ Pags);
}
if (unlikely(dx_metatype(dq) != _DISPATCH_} ! u 1 y LANE_TYPE)) {
DISPAT^ R i E CH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");
}
dispatch_lane_t dl = upcast(dq)._dl;
// Global concurrent qu1 U B n : 3 E ]eues` 8 - and queues bound to non-dispatch thread@ 1 rs
// always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
if (unlikeli & S ~y(!_dispatch_qJ a f ^ zueue_try_reserve_sync_width(dl))) {
return _dix j a zspatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);
}
i# H w ? ( sf (unlikely(dq->do_targetq->do_targetq)) {
returnY y _di` p , q Sspatch_sync_recurse(dl, ctxt, func, dc? Z r M 4_flags);
}
_dispatch_introspection_sync_begin(dl);
_dispatch_sync_invoke_and_complete(dl, ctxt, func DIa 1 g t s W 6SPATCH_TRACE_ARG(
_dispatch_trace_item_sync_push_pop(dq, ctxt, fun3 - P ~ B gc, dc_flags)));
}c I x 8 ]
4._dispatch_barre N 0 K ~ier_sync_f
串行行列
和栅门函数
的比较相似,所以跳转到这儿,仍是中间层封装
DISPATCH_N| + C eOINLINE
static vX A !oid
_dispatch_barrier_synT K X M U ) 4 oc_f(s e j G z 6 s rdispatch_queue_t dq, void *ctxt,
dispatch_u ^ Afunction_ti x q z t _ s ^ 0 func, uintptr_t dc_flags)
{
_dispatch_barrier_sync_f_inline(dq, ctxt, func, dc_flags);
}
5._dispatch_barrier_sync_f_inline
DISPATCH_ALWAYS_INLINE
stati? 1 t & M %c inline von o * q ^ _ + Qid
_dispatch_barri8 O a 7 J Fer_sync_f_inline(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func, uintptr_t dc_flags)
{
dispatch p vh_tid tid =1 f # * _dispatch_tid_self();
if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {
DISPATCH_CLIENT_CRASHk P k(0, A | B I p @"Queue type doesn't supports D z p a ! J dispatch_sync");
}
dispatch_lane_t dl = upcast(dq)._dl;
// The more correct thing tg c Fo do would be to merge the qos of the thread
// that just acquired the barrier lock into the quu l R Q C q @eue state.
//
// However this is too expensive for theh f m y * ) h fast path, so skip doi0 ~ . S ]ng it.
// The choseF h . R t c in tradeoff is that if an enqueueH Q p E l ( 2 u on a lower priority thread
// contends with this fau ? = nst path, this thread may receive a useless override.
//
// Global concurrent queues and queues bound to non-dispatch threads
// always f. $ q # 3all into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
// 死锁
if (unlikely(!_dispatch_queue_try_acquire_barrier_sG I * s [ 1 #yM } 0 [ y Hnc(dl, tid))) {
rG V w i b Deturn _dispatch_sync_f_slow(dl, ctxt, func, DC_FLAG_BARRIER, dl,
DC_FLA$ g ~ & aG_BARRIER | dcn 5 ; k Q k . m U_flags);
}
if (unlikely(dl->do_targetr 5 * Vq->do_targetq)) {
return _dispatch_sync_recurse(dl, ctxt, func,
DC_FLAG_BARRIER | dc_flags);
}
_dispatch_introspection_sync_begin(dl);
_dispatch_lane! E 9 4_barrier_sync_invoke_and_complete(dl, ctxt, func
DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(
dq, ctxt, func,# I a m dc_flags | DC_FLAG_Bo 4 d lARRIER))+ B ; ? v M);
}
5.1 _dispatch_tid_self
_dispJ ? o katch_tid_self
是个宏界说,终究调用_dispatch_thread_getspecific
来获取当时线程id(线程是以key-value
的方式存在的)
#define _dispatch_tid_self() ((dispatch_tid)_dispatch_thread_port& } M ; r Z _ k())
#if TARGE5 o & MT_OS_WIN32
#define _dispatc[ $ z ) B 7 Rh_thread_port() ((maJ % ich_port_t)0)
#elif !DISPATCH_USE_THREAD5 ? 2 a_LOCAL_STORAGE
#if DISPATCH_USE_DIRECT_TSD
#define _dispatch_thread_porQ e otU | 3 Y () ((mach_port_t)(uintptr_t)
_dispatch_thread_gets) 7 Epecific(_PTHREAD_TSD_SLOT_MACH_THREAD_SELF))
#else
#define _dispatch_thread_port() pthr 2 & G Nead_mach_thread_np(_dispatch_thF ~ t k c b 8read_sa @ 4 !elf())
#endif
#endif
是时分扮演真正的技能了!接下来便w M 2 8 G } ] # 8是死锁的中心剖析!
5.2 _dispatch_queue_try_acquire_barrier_sync
_dispatch_queue_try_acquire_barrier_sync
会从os底层获取到K g b 2 * /一个状况
DISPATCH_ALWh 8 & ) , Y rAYS_INLINE DISPATCH_WARN_RESULT
static inline bool
_dispatch_queue_try_acquire_barrier_sync(dispatch_queue_class_t dq, uint32_t tid)- p ;
{
return _dispatch_queue_trb ` 4 ) Qy_acquire_barrier_sync_and_suspend(dq._dl, tid, 0);
}
DISPATCH_ALWAYS_INLINE DISPATCH_WARN_RESULT
static inline bool
_dispatch_queue_try_acquire_barrier_@ z M ^ f B D 7 .synct v Z K o g b_andm L = j /_suspend(dispatch_lane_t dq,
uintB a @ @ ,32_t tid, uint64_t suspend_count)
{
ui T {nt64_t init = DISPATCH_QUEUE_STATE_INIT_VALUE(dq->dq_width);
uint64_t vas o wlue = DISPATCH_QUEUE_WIDTH_FULL_BIT | DISPATCH_QU6 V { 4EUE_IN_BARRIER |
_dispatg ! P * g g _ch_lock_valuz v 7 i ! + C d Ge_from4 + J L W :_tid(tid) |
(suspe` :nd_cop s 1 ] c eunt * DISPATCH_QUEUE_SUSPEND_INTERVAL);
uint64_t old_state, new_state;
// 从底层获取信息 -- 状v z R : 9 )况信息 - 当时行列 - 线程
return os_atomic_rmw_loop2o(dq, dq_state,; p y { | R E old_state, new_state, acquire, {
uint64_t role = old_st7 R q # nate & DISPAm x $ ~ K *TCH_QUEUE_ROLE_MASK;
if (old_state != (init | role)) {
os_atomic_rmw_loop_givt O e } u ge_up(break);
}
new. u N - G { & ,_state = value | role;
});z K : 0 [
}
5.3 _dispatch_sync} M a +_f_slow
在5.2
获取到new_stQ f m 6 ate
就会来到这儿(死锁溃散时的调用栈中就有这个函数)
DISPATCH_NOINLINE
static void
_dispatch_sy, ! p F nc_f_s- J 6 a B (low(dispatch_J , ? v K mqueue_class_t top_dqu, void *ctxt,
dispatch_functionK ! 7 Y o T v_t fun) P @c, u[ L 6intptr_t top_dc_flags,
dispatch_queue_class_t dqu, uintptr_t dD ^ o } x l y Ic* 6 | 3 h & ! d l_flags)
{
dispatch_queue_t top_dq = top_dqu._dq;
dispatch_queue_t dq = dqu._dq;
if (unlikely(!dq->do_targetq)) {
return _dispa, ? C M +tch_sync_functic V r 2on_invoo : 5 4 X rke(dq, ctxt, funcG = E j %);
}
pthread_p& c lriori6 [ V { # # 6 Ety_t pp = _dispatch_get_priority();
struct dispatch_sync_context_s dsc = {
.dc_flags = DK a E eC_FLAG_SYNC_WAITER | dc_flags,
.dc_func = _dispatch_async_and_wait_invoke,
.dc_ctxt = &dsc,
.d9 ^ $ v z k w d (c_other = top_dq,
.dc_priority = pp | _PTHREAD_PRIORITY_ENFORCE_FLAG,
.dc_voucher = _voucher_get(),
.dscK y I : _ 5 R v M_func = func,
.dsc_ctxt = ctxt,
.dsc_waiter = _dispatch_tid_self(),
};
_dispatch_trace_itZ = E * W { !em_push(top_dq, &dsc);
__DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);
if (dsc.dsc_func == NULL) {
dispatc4 , & 5 | 3h_queue_t stop_dq = d1 m e Esc.dc_other;
return _dispatch_sync_complete_recurse(top_dq, stop_dq, top_dc_flags);
}
_dispatch_introspection_sync_begin(top_dq);
_dispatch_trace_item_pop(top_dq, &dsc);
_dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags
DISPATCH_TRACE_ARW ~ | }G(&dsc));
}
-
_dispatch_traca X T s Ie_item_pusV n t q 9 2h
压栈操作,将履行使命push到行列中,依照FIFO
履行 -
__DISPATCH_WA; k x } b [ fIT_FOR_QUEUE__
是溃散栈的最终一个函数
DISPAs o D & , Z l !TCH_NOINLINE} o Q
static void
__DISPATCH_WAIT_FOR_QUEUE__(dZ 5 X C d A X ] 6ispatch_sync_context_t dsc, dispatch_queuer h M @ ! k Y f_t dq)
{
// 获取行列的状况,看是否是处于等候状况
uint64_t dq_state = _dispatch_wait_prepare(dq);
if$ H p e L (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");
}
...
}
5.4 _dq_state_drain_loc/ _ ~ v Dked_byA ( z
比较当时等候的va= N u }lue
和线程tX # aid
,假如为YES就回来回去进行报错处理
DISPATCH_ALWAYS_INLINE
stat* . 5 mic inline bo_ J g V jol
_dq_s] 5 d J ; 1 Mtate_drain_locked_by(uintI 1 N T64_t dq_state, dispatch_tid tid)D N n % E B
{
return _dispatcZ % s F D P M jh_lock_is_locked_by((dispatch_lock)dq_v U 1 8 v 3 V Tstate, tid);
}
DIk 0 b !SPATCH_ALWAYS_INLINE
static inline bool
_dispatch_lock_is_locked_by(? 3 x l X o J tdispatch_lock lock_value, dispatch_tid tid)
{
// equivalent to _dispat3 ^ M - Xch_lock_owner(lock_value) == tid
// ^b S v - & m 6 (异或运算法) 两个相同就会出现 0 否则为1
return ((lock_va} [ ] x lue ^ tid) & DLOCK_OWNER_MASK) == 0;
}
6.一图看懂死锁流程
- 死锁的发生是
线程tid
和当时等候状况转换后的value
作比较 - 同步履行
dispatch_sync
会进行压栈操作,依照FIFO
去履行 -
栅门函数
和同步履行
是差不多的
三、dispatch_block使命的履行
在di+ O L g ? m ( cspatch_block
处打下断点,LLDB调试打印出函数调用# ; r $ 9 n m栈
1._dispatch_lane_barrier_sync_[ } einvoke_and_complete
这儿也采用了类似于上文中的os底层回调,至于为什么用回调——使命k L d S I ^的履行依赖于线程的状况,假如线程状况. : | o @ x不行良好的话使命不会履行
DISPATCH_NOINLINE
static void
_dispatch_lane_barrier_sync_invoke_and_complete(dispatch_lane_t dq,
voidg C F Q y f g *ctxt, dispatch_fuP C @ h 6 1 Snction_t func DIS@ g S ] rPATCH_TRACE_A ) H ; ( CRG(void *dc))
{
...
// similar to _dispatch_queue_drain_try_unlocke @ Q
os_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, releaseo 8 ? 4 d j, {
new_state = old_state - DIZ E { F $ b W h }SPATCH_QU} N @ U REUE_SERIAL_DRAIN_OWNED;
new_state &= ~DISPATCH_QUEUE_DG W Z 8RAIN_UNLOCK_MASK;
new_statn } je &= ~DISPATCH_QUEUE_MAX8 S P a y_QOS_MASK;
if (unlikK R Q A f wely(old_state & fail_unlock_mask)) {
os_] V d ratomic_rmw_loop_give_up({? Z 4 R q a 8 % d
return _dispatch_lane_barrier_complete(dq, 0, 0);
});
}
});
if (_dq_state_is_base_wlh(old_state)) {
_dispatch_event_loop_assert_not_owned((dispatch_wlh_t)dq);
}
}
- _dispatch_lane_barrier_complete
直接跟到_dispatch_lane_class_bE ) E H l , + varrier_complete
DISPATCH_NOINLINE
static voidB M E A r h @ *
_dispatchf , V C Z ] I_lane_bW ) y 4 I g barrier_complete(dispatch_lane_class_t dqu, dispatch_qos_t qos,
dispatch_wakeup_= 9 p U c b cflags_t flags)
{
...
uint64_t owned = DI/ L u ^ : G [SPATCH_QUEUE_IN_BARRIERI 1 b 2 % +
dq->dq_width * DISPATCH_QUEUE_WIDTH_INTERVC b J JAL;
return _dispatch_lane_class_barrier_complete(dq, qos, flags, target, owned);
}
- _dispatch_= 2 | O | } ! H hlane_class_barrier_complete
跟进_dispatch_queue_push_queue
DISPATCH_NOINLINE
static void
_dispatch_lane_class_barrier_complete(dispatch_lane_t dq, dispatch_qos_t qos,
dispatch_wakeup_flags_t flags, dispatchg M 9 E_queue_wakeup_target_t target,
uint64_t owned)
{
...
if (tq) {
if (likely((old_state ^ new_state) &a2 Y e Tmp; enqueue)). e z z {
di* } M E spatch_assert(_dl 0 Z B Wq_state_is_enqueued(new_state));
dispatch_assert(flagsc d 0 & DISPATCH_WAKE4 q ` ` hUP_CONSUME_2);
return _dispa! ^ ^ M , * n Xtch_queue_puQ $ 3 8sh_queue_ T r % , N T ?(tq, dq, new_state);
}
.P 1 C C..
}
}
- _dispatch_queue_push_queue
而其间的dx_push
是个宏界说
#define dx_push(x,f a : ; : y, z) dx_vtable(` T : Z lx)->dq_[ m D Z kpush(x, y, z)
DISPATCH_ALWAYS_INLINE
static inlinj L 1 @ He void
_dispatch_quX v f . U K O [eue_push_queue(dispatch_queue_t tq, dispatch_queue_class_t dq,
uint64_t dq_state)
{
_dispatch_trace_item_push(tq, dq);
return dx_push(tq, dq,T J ! ) 5 h 1 _dq_state_mS : h cax_qos(dq_state));
}
-
dq_push
大局查找来到dq_push
,选择_dispatch_root_queue_push
持续走下去 -
_dispatch_root_queue_p` Z g . _ushz 9 [
大概率会走_diy ) 8 V ` X m M -spatch_root_queue_push_inline
DISPATCH_NOINLINE
void
_dispatch_root_queue_push(dispatch_queH ^ * i ( ` a { Wue_global_t rq, dispat7 p o l K (ch_ob@ o y v Uject_t dou,
dispatch_qos_t qos)
{
...
#if HAVE_PTHREAD_WORKQUEUE_QOS
if (_dispatch_root_queue_push_needs_ove. S E O 8 U C Drride(rq, qos)) {
return _dispatch_root_d M n queue_push_override(rq, dou, qos);
}
#else
(void)qos;
#endif
_dispatch_root_queue_push_inlinh ~ Ge(rq, dou, dou, 1);
}
- _dispatch_root_queue_push_inline
DG [ * . ( - K dI r u 3 I P { s ]SPATCH_ALWAYS_INLINE
static inline void
_dispatch_root_queue_push_inline(dispatch_queue_0 * S %global_t dq,
dispatch_object_t _head, dispatch_obj3 Y l ) &ect_t _tail, int n)
{
struct dispatch_object_s *hd = _head._do, *tl = _tailG W p G P._do;
if (unlikely(os_F ^ ] * Umpsc_push_list(os_mU N } Z 3psc(0 n u / R 0dq, dq_items), hd, tl, do_next))) {
return _dispatch_root_queue_poke(M X p $ sdq, n, 0);
}
}a U L 7 z c e v 1
- _dispatch_root_queue_poke
DISPATCH_NOINLINE
void
_dispatch_root_queue` V W $ B_poke(dispatch_queue_global_t dq, int n, int floor)
{
...
returB C i xn _dispaL ^ T 7 C vtch_root_queue_poke_slo^ W N a 6 2 & $w(dq, n, flo8 ) - K (or);
}
- _dispatch_root_queue_poke_slow
DISPATCH_NOINLINE
static void
_dispatch_root_queue_poke_sv ; low(dispatcB p ch_queue_global_t dq, int n, int floor)
{
int remai3 } G 4ning = n;
int r = ENOSYS;
_dispatch_root_queues_init();e I .
_dispatch_debug_root_queue(dq, __func__);
_dispatch_trace_runtim2 u f y 6e_event(wo 9 9rker_request, dq, (uint64_t)n);
...
}
- _dispatch_root_queues_init
跟到中心办法dispatch_once_f
static inline v: ^ N ; ` C ^oid
_dispatch_root_queues_init(void)
{
dispatch_once_f(&_dispatch_root_5 l I 9 q Gqueues_prd p ; Q q V +ed, NULL,
_dispatch_root_queues_init_once);
}
- dispatch_once_f
当你看到_dispatch_once_callout
函数就离成7 / F 3 9 0 c ,功不远了
DISPATCH_NOINLINE
void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatr 0 b $ } ; l o $ch_funct] [ 5 k V K Cion_t func)
{
// 假如你来过一次 -- 下次就不来
dispatch_once_gate_t l = (dispatch_once_gate_t)v7 L 3al;
//DLOCK_ONCE_DONE
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
uintptr_t v = os_atomic_load(&l->dgo_once, ac@ c f fquire);
if (likely; Z 4 3 f(v == DLOCK_ONCE_DONE)) {
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
iK T C 7 )f (likely(DISPATCH_ONCE_IS_GEN(v))) {
return _dispatch_once_mark_dog : [ne_if_quiesced(l, v);
}
#endif
#endif
// 满足条件 -- 8 * 企图进去
if (_d` S | A A { s 4ispatch_o. i E # % S g S unce_gate_tryentt X ; Rer(l)) {
// 单利调用 -- v->DLOCK_ONCE_DONE
return _dispatc{ * - = { ih_oncw q j 3e_callout(l, ctxts K u t a D y C, func);
}
return _dQ w E V i ~ispatch_once_wait(l);
}
- _dispatch_once_callout
DISPATCH_NOINLINE
static void
_dispatch_once_callou$ 5 e ^ ,t(dispa; V [ g +tch_once_gate_t l, void *ctxt,
dispatch_functir a J ; von_t func)
{
_dispatch_client_calloutf R g D c S(ctxt, func);
_disF 8 & Z ) : Ipatch_once_gate_broadcast(l);
}
2._dispatch_client_callout
f(ctxt)p K O c W s ~ e P
调用履行dispatch_f! Q 9 r C D _unction_t
——dispatch_block
的履行点
DISPATCH_NOINLINE
void
_dispatM r 3 4ch_clienG J y m - / Jt_callout(void *ctxtJ + y v, dispatch_function_t f)
{
_dispatch_g+ z & get_tsd_base();
void *u = _u 6 ~ % g B 1 ,dispatch_get_unwind_tsd()h : N J;
if (likely(!u)) returZ W 2 y dn f(ctxt);
_dispatch_set_unwind_tsd(NULL);
f(ctxt);
_dispatch_free_unwind_tsd();
_dispatch_set_unwind_tsdT 0 N 6 z C =(u);
}
3.一图看懂使命保存流程
最终的最终咱v p b :们找到了使命履行点,但+ ` h没有找到使命的保存点,接下来就要从同步函数和异步函数说起了
四、同步函数
前文中现已跟过dispQ t M H . l 1 Catch_sync
的实现了,这儿上一张图再捋一捋(特别注意work和func的调用K X T)
-
串行行列
走d* _ x 0 =q->dq_width == 1
分支-
_dispatch_barrier_sync_f
->_dispatch_G - = jbarrier_sync_f_inline
->_dispatch_lane_barrier_sync_invoke_and_complete
- 然后便是
三、dispatch_block使命的履行
中的流程
-
- 其他状况大概率走
_dispatch_sync& C 5 j g O I_invoke_and_complete
1._. w e , 1diss e m m dpaC Q $ [ Wtch_sync_invt 7 ( z 1 K 2oke_and_complete
保存func
并调9 } m g用_dispatch_sync_func` ; M j P k ^tion_invd b #oke_inline
DISPATCH_NOINLINE
staticP s X void
_dispaF $ | F n Wtch_sync_invoke_and_completE S z z } | P _ je(dispag $ | /tch_lane_t dq- } Z * & c, void *ctxt,
dispatch_function_t func DISPATCH_TRACE_ARG(void *dc))
{
_dispatch_sync_function_invoke_a { z |inline(dq, ctxt, func);
_dispatch_, D =tre r W P e a e Yace_item_complete(d& 0 ! ~ D Z O , [c);) 2 Q
_dispatch_lane_non_barrier_complete(dq, 0);
}
2._dispatch_sync_function_invoke_inline
直接调用_dispatch_client_callout
与三、dispatch_block使命的履行
遥遥相对
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sync_function_invoke_inline(dispatch_queue_class_t dq, void *h O 8 n G 8 *ctxt,
dispatch_function_t func)
{
dispatch_thread_frame_s dtf;M F ^
_dispatch_thread_frame_push(&dtf, dqC ~ M / J I H);
// f(ctxt) -- func(ctxt)
_dispatch_client_callout(ctw ~ v z H ,xt, func);
_dispatch_perfmon_workitem_inc();
_dispatch_thread_frame_pop(&d H h ? e x 9amp;dtf);
}
3.一图看懂同步h l C V E ~ 4函数履行的部分流程
五、异步函数
1. 使命的保存
仍是相同的思路,跟到dispatch_async
的源码实现中,关注df q oiC W lspatch_block_t
1.1 dispatch_async
void
dispatch_async(dispatch_queue_t dq, dispatch_blocW 6 o I ~ _ `k_t work)
{
dispatch_continuation_t dc = _dispatch_continZ ! t Y g Ouation_alloc();
uintptr_t dc_flags = DC_r ! Q LFLAG_CONSUME;
dz x +ispatch_qos_t qos;
qos = _dispatch_continuatix W v } H +on_init(dc, dq, work, 0, dc_flags);
_dia C M g 5spatch_continuation_async(dq, dc, qos, dc->dc_flags)W 2 j H y Z;
}
1.2 _dispatcy B Y , c 6h_continuation_init
_dispatch_Block_invoke
将使命进行一致格式化
DISPA? K ) T g / @ O UTCH_ALWAYS_INLINE
static inline dispatch_qos_t
_dispatch_continuati) . & & 6 ; g Son_init(dispatch_contiR o Hnu; % r : m U { ] kation_t dc,
dispatch_queue_class_t dqu, dispatch3 & g e_block_t work,
dispatcv w # + g ,h_block_flags_t flags, uintptr_t dc_f| Z 2 ~lags)
{
void *ctxt = _dispatch_Block_copy(wQ U r rork);
dc_flags |= DC_FLAG_BLOCK | DC_FL= @ NAG_ALLOCATED;
if (unlikely(_dispatch_block_has_private_data(work))) {
dc->dc_flags = dc_flags;
dc->dc_ctxt = ctxt;
// will iniV 6 R m j u ctialiW c = $ I x %ze all fieO g P ]lds but requires dh : E h %c_flags & dc_ctxt to be set
rt 6 Leturn _d] X + ` G T w oispatch_continuation_init_slow(dc, dqu, flags);
}
dispatch_function_t func = _dispatch_Block_invoke(work);
if (dc_flags & DC_FLAG_CONSUME) {T N # x q Z D F ;
func = _dispatch_call_block_and_release;
}
return _dispatch_continuation_init_f(dc, dqu, ctxt, ff ( - } ?unc, flags,V C 6 4 N ; dc_4 n aflags);
}
1.3 _dispatch_continuation_initX : G t #_f
dc->dc_func = f
将block使命 保存下来
DISPATCH_ALWAYS_INLINE
static inlineq M 3 w ~ dispatch_qos_t
_dispatch_continua= U m ? 9 l wtion_init_f(dispatch_continuation_t dc,
dispatch_queue_class_t dqu, void *ctxt, dispatch_function_t f,
dispatch_block_flags_t flags, uintptr_t dc_fR 7 F 7 0 wlags)
{
pthread_priority_t pp = 0;
dc->dc_flags = dc_flags | DC_FLAG_ALLOCATED;
dc->dc_func = f;
dc->dc_ctxt = ctxt;
// in this context DISPATCH_BLOCK_HAS_PRIORITY means thQ a 5 cat the prioj H ority
// should not be propagath X y a red, only taken frox s 2 4 } j a C Gm the haP z Bndler if it has one
if (!(flags & DISPATCH_BLOCK_HAS_PRIORITY)) {
pp = _dispatch_priority_propagate();
}
_dispatch_continuation_voucher_set(dc, flags);
return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
}
异步函数的使命保存` 8 / g y 7找到了,那它的使命又是何时履行的呢?以及线程是何时创立的?
2. 线程的创立
2.1 _dispatch_continuati7 : P b B @ _ x |on_async
DISPATCH_AL! F rWAYS_INLINE
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
dispatch_continuation_t dc, disp( g Y p Batch_qos_t qos, uintptr_t dc_flags)
{
#if DISPATCH_INTROSPECTION
if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
_diss c m c Z {patcl $ 7h_trace_item_pu0 r l 0 5 i }sh(dqu, dc);
}
#else
(void)dc_flags;
#endif
return dx_push(dqu._dq, dc, qos);
}
2.1 dx_push..n 9 5 x M M.
之前现已剖析过了,至于为什么要用_dispatch_root_queue_push
研讨——因为它最根本,省去了旁枝末节
dx_push
-&! ^ Y (gt;dq_F d h D D zpE G ) T D * U Yush
->_dispatch_root_queue_push
->_dispatch_root_queue_push_inline
->_dispatch M S . G M ~ h_root_queue_poke
->_dispatch_root_queue_pk a V 0 [ = L z Koke_slow
2.2 _dispatch_root9 j , 6 : J U R_queue_poke_slow
static void
_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)
{
...
// floor 为 0,remaining 是根据行h @ : ) k f j ?列使命的状况处理的
int can_request, t_coW Z l N bunt;
// 获取线程池的巨细
t_count = os_atomic_load2o(dq, dgq_thread_pool_size, ordered);: , O w s
do {
// 核算能够请求的数量^ e D N k
can_request = t_count < floor ? 0 : t_count - floor;
if (remaining > can_i X ^ + y #request)i G e C I _ {
_dispatch_root_queue_debug("pthread pool reducing request f# | m (rom %d to %d",
remaining, can_request);
os_atomic_sub2o(dq, dgq_pending, remaining - can_request, relaxed);
remaining = can_ry o d e } iequest;
}
if (remaining == 0) {
// 线程池满了n ; _ 0 S,就会报出反常的状况
_dispatch_root_que4 k oue_debug("pthread pool is full for root queue: "
"%p", dq)[ J l L ~;
return;
}
} while (!os_atomic_cmpxchgvw2o(dq, dgq_thread_pool_size, t_count,
t_count - remaining, &t_c~ S u 0 N { ) ]ount, acquire));
pthread_attr_t *attr = &pqc->dpq_thread_{ 3 # 1attr;
pthread_t tid, *pthr = &tid;
#if DISPATCH_USE_MGR_THREAD && DISPATCH_USE_PTHREAD_ROOT_QUEUES
if (unlikely(dq == &_dispatch` = w_mgr_root_queue)) {
pthr = _div : d : T j m `spatch_mgr_root_} s / +queue_init();
}
#endif
do {
_dispatch_retain(dq);
// 拓荒线程
whi/ & L u h !le ((r = pthread_create(pthr, attr, _dispy e X 5 O d , 4at5 # G 4 } $ 1 # Uch_wo) y *rkeC 6 2 i E 7r_thread, dq))) {
if (r != EAGAIN) {
(void)dispatch_asr I t !sume_zero(r);
}
_diw R M 8 0spatch_temporary_resource[ _ L ^ D h 1 U ?_shortage();
}
} while (--remaining);
#else
(void)floor;
#endif // DISPATCH_USE_PV , , kTHREAD_POOL
}
-
第一个
do-while
是对中心线程数的判别、操作等等 -
第二个
do-while
调用pthread_create
创立线程(底层仍是用了pthread
)
3.使命的履行
使命的履行其实刚才现已讲过了
_dispatch_root_queueL - b ^ ] as_init
->dispatch_once_f
->_dispatch_once_callout
->_dispatch_client_caP D v ) + - , tllout
只不过使命在等候线程的状况,而线程怎样履行使命就不得而知了
4.一图看懂异步函数的履行流程
六、信号量的原理
信号量的根本运用是这样的,底层又是怎样个原理呢?
dispatch_semaphore_t sem = dispatch_semaphore_crea~ k ` !te(0);
dispatQ [ 1 X ) l kch_s* @ remaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(sem);
1.dispatch_semaphore_create
只是初始化dispaw * d o 6 (tch_semaphore_t
,内部进e _ G h L行传值保存(value必须大于0)
dispatch_semaphore_t
diE 1 O * ( * Z aspatch_semaphore_create(long value)
{
dispatch_semaphore_t dsema;
// If the internal value is negative, then the absolute of the value is
// equal to the number of waitingf 1 = z ^ V threads. Therefore it is bo3 7 V _ w m Zgus to
/} a Z ~ ^ $ 2 & ?/ iJ y m Q $nitialize the semaphore with a negative value.
if (valp A Zue < 0) {
return DISPATCH_BAD_INPUT;
}
dsema = _dispatch_object_alloc(DISPATCH_VTs H ]ABLE(semaphore),
sizeof(struct dispatch_semaphore_s));
dsema->do_next = DISPATCH_OBJECT_LISTLESS;
dsema->. $ 9 = M ,do_targetq = _dispatch_get_default_que} c 4 / ~ F A Cue(false);
dsema->d+ # . 8 Wsema_valuez R K Q : H y j r = value;
_dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
dsema->dsema_orig = vS y Salue;
return dsema;
}
2.dispatch_semaphore_signal
类似KVC
方式从底层[ 8 K u取得当时信号量的value
值F + s ! t N / – @,而且这个b d ; ? M K w函数是有回来值的
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
long value = os_atomic_inc2ox 4 v u : / % d U(dsem* g c 5 i La, dsema_value, release);
if (likely(value > 0)) {
return 0;
}
if (unlikely(value == LONG_MIN)) {
DISPATCH_& ` CLIENT_CRASH(value,
"Unbalanced call to dispatG - I 5 Sch_semaphore_signal()");
}
return _dispatch_semaphore_signal_slow(dsema);
}
DISPATCH_NOINLINE
long
_diu P E z j F R wspatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
_dispatch_sema4_create(&dsema->dsema_F * T / Fsema, _DSEMA4_PO] . wLICY_FIFO);
_dispatch_sema4_signa0 a @ -l(&dsem0 | W 4 = O 3a->dsema_f 0 C f * , isema, 1);
return 1;
}
其实最中心的点在于os_atomic_inc2o
进行了++操作
#define os_atomic_inc2o(p, f, m)
os_atomic_add2o(p, f, 1, m)
#define os_atomic_add2o(pK h } @ h m, f, v, m)
os_atomic_add(&R X 8 R;(p)->f, (v), m)
#de8 Z ffine os_atoV h Tmic_add(p, v, m)
_os_atomic_c11_op(, Z ( [ c ! E d(p)n # s , ~ q # m }, (v), m,E } a ; p B addD t E, +)
3.dispatch_semaphore_wait
同理dispatch_p ] 0 3 Q 0 gsemaphore_wait
也是取value值,并回来对应成果
-
vB : m 6 n * {alue>=0
就马上回来 -
value<0
根据等候时间timeout
作出不同操作-
DISPATCH_TIME_NOW
将value
加一(也便是变为0)——为了抵消 wait 函数一开始的减一操 V ] f作,并回来KERN_OPEH 1 a ! Y [RATION_TIMED3 ] s B % X_OUT
表明因为等候时间5 = b r =超时 -
DISPATCH_TIME_FOREVER
调用系统的semaphore_wait
办法持续等候,直到收到signal
调用 -
默认状况
与DISPATCH_TIME_FOREVER
类似,不过需求指s } o a 4 ? l定一个等候时间
-
long
dispatch_semaphore_& x +wait(dispatch_semaphor P 3 _ i ^e_t dsema, dispatch_time_t timeout)
{
long value = os_atomic_dec2o(dsema, dse3 p , ( i R G `ma_value, acquire);
if= O h 3 o t l (likely(value >= 0)o R $ o W) {
return 0;
}
return _dispatch_semaphore_wait_slow(dsema, timeout);
}
DISPATCH_NOINLINE
static long
_dispatch_semaphore_wait_slow(diD w 8 ; @ ; 6spatchI , H s_semaphore_t dsema,
dispatch_tim? G 3 & c 2e_t timeout)
{
long orig;
_dispatch_sema4_create(&_ N 9 l W J I p S;dsema->dsema_sema, _DSEMA4_G * qPOLICY_FIFO);
switch (timeout) {
default:
if (!e R s C e ` s_dispatch_sema4_timedwait(&# $ Y ` l ,;dsema->dsm | r ( ` Cema_sema, timeout)) {
break;
}
// Fall through and try to undo what the fast path did tL M ] n ko
// dsema->dsema_value
case DISPATCH_TIME_NOW:
orig = dsema->dsema_value;
while (orig < 0) {
if (os_atomic_cmpxchg+ J q [ *vw2o(dsema, dsema_value, orig, orig + 1,
&A + T e ^ 3amp;orig, relaxed)) {
return _DSEMA4_TIMEOUT();
}
}
// AnoK O ^ther thread called semaphore_signal().
// Fall through and drain the wakeup.
case DISPATCH_TIME_FOREVER:
_dispatch_semab 0 1 Z 1 R4_wB f I U Jait(&dsema->dsema_sema);
break;
}
return 0;
}
os_atomic_dec2o
进行了--操作
#define os_atomic_dec2o($ ; t z 5 5 v o @p, f, m)
os_atomic_sub2o(p, f, 1, m)
#define os_atomic_sub2o(p, f, v,A ( ? I | R H m)
os_atomic_sub(&(p)->f, (v), m)
#define os_a: k ! O % )tomic_sub(p, v, m)
_os_atomic_c11_op((p), (v), m, sub, -)
七、调度组的原理
调度组的根本运用如下
dispatch_V N ~ 8group_t group = dispatch_group_create();
dip t Q f U 3 & +spatch_group_enter(group);
dispatch_group_U k , Q s yleave(R - c l E d c W 4group);
dispatch_group_async(group, queue, ^{});
dispatch_gr; U + . I j a Youp_notify(group, queue, ^{});
1.dispatch_group_create
跟其他GCD目标相同运用_dispatcX ^ +h_object_alloc
生成dispatch_group_t
从os_atomic_store2o
能够看出group
底层也维护了一个value
值
dispatch_group_t
dispatch_group_create(void)
{
return _dispatch_group_create_with_count(0);
}
DISPATCH_ALWAYS_INLINE
static inline dispatch_group_t
_dispatch_group_c8 4 K L e FreatM . # B m Re_with_count(uint32_t n)
{
dispatch_group_t dg = _di. A ]spatch_object_alloc(DISPATCH_VTABLE(group),
s! q gizeof(struct dispatch_group_s));
dg-X | w S o v 9 `>do_next = DISPATCH_OBJECT_d g 0 T { 3 bLISTLESS;
dg->do_targetq = _dispatch_get_default_queue(false);
if (n) {
os_atomic_store2o(P E P l A z j Adg, dg_bits,
-n * DISPATCH_GROW @ m U = Z F $UP_VALUE_INTERVAL, relaxed)t ; R p $;
os_atomic_store2o(dg, do_ref_cnt, 1, relaxed); // <rdaX $ U g Jr://22318411>
}
return dg;
}
2.dispatx ; L E u * Y I rch_group_enter & dispatch_group_leave
这两个API
与信号量的I K % [ /运用大Q M ^ E x n =同小异,os_atomic_sub_orig2o
、os_atomic_add_orig2o
担任--
、++
操作,假如不成对运用} x D y 7 0 b 5则会出错
-
dispatch_group_leave
出组会对state
进行更新 - 全部出组了会调用
_dispatch_group_wake
void
dispatch_group_enter(disd y = 6 | b G g $patch_group_t dg)
{
// The valh 5 r ] D z ?ue 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, acquirez ` 0);
uint32_t old_value = oldp D ] * H H 9 ) D_bits & DISPATCH_GROUP_VALUE_MC M 3 | w v s 9ASK;
if (unlikely(old_value == 0)) {
_df x 7 h Aispatch_reta? * { L M { *in(dg); // <rdar://problem/22318411>
}
if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {
DISPATCH_CLIEN* U } y L , 8 ) ;T_CS V xRASH(old_bits,
"Too many nested calls toT v { M # a dispatch_grV 2 @ o N s &oup_enter()");
}
}
void
dispatch_group_leave(dispatch_group_t dg)
{
// The value is incremented on a 64bits wide atomic so that the cl ` [ Zarry for
// the -1 ->P ^ ? = = ! 0 transition increments the generation atomically.
uint64_t new_state, old_state = osq z K B - & R_atom9 } G = h xic_add_orig2o(dg, dg_state,
DISPATCH_GROUP_VALA @ . J k W `UE_INTERVAL, release);
uintY Q v32_t old_value = (uint32_tA E , ( E f 5)(old_state & DISPATCH_GROQ y n 8 k + #UP_VALUE_MASK);
if (unlikelyh ( 8 + M k M(old_value == DISPATCH_GROUP_VALUE_1)) {
old_state += DISPATCH_GROUP_VALUE_INTERV 5 y @ l c TAL;
do {
new_sM O 4tate = oldK [ N } i n W_state;
if ((old_state &= { Y A H 3amp; DISPATCH_GROUP_VALUE_MASK) == 0) {
new_state &= ~DISPATCH_GROUP_HAS_WAITE0 D S 8 o f 3 K -RS;
new_state &= ~DG ( { 7 kISPATCH_GROUP_HAS_NOTIFS;{ h v 7 6 ; s V [
} else {
// If the group was entered again since the atomic_add above,
// we can'to / * k w J ; clear the waiters bit anymore as we don't know for
// which generation` a j the waiters are for
new_state &= ~f ; : 6 6DISPATCH_GROUP_HAS_NOTIFS;
}
if (old_staZ K J & rte == new_state) break;
} while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,
old_sE * P m f w Z Htate, new_{ O e j = n 5 3 Vstate, &old_state, relaxed)) a O v S P J 7 s);
return _dispatch_group_w+ V X aake(dg, old c m Pd_state, true);
}
if (unlikely(old_value == 0)) {
DISPATCH_CLIENTN N $ w } % a V_CRASH((uintptr_t)old_value,
"Unbalanced call to dispatch_group_leave()");
}
}
3.dispatch_group_async
-
_dispatch_continS $ v j ^ &uM m / C n N n #a~ R %tion_init_f
保存使命(类似异步函数) - 调用
_dispatch_continuation& W $ $ [ P Q 1_group_async
voC { ` D w bid
dispatch_group_async_f(dispatch_0 t M % B = U 8 xgroup_t dg, dij { = c :spatch_queue_t dq, v| * p = C Koid *ctxt,
dispatch_function_t func)
{
dispatch_continuation_t dP ; C f v ; H x ac = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_C] p : i H T / C dONSUME | DC_FLAG_GROUP_ASYNC;
dispatch_qo| G f ^ : l V G )s_t qos;
qos = _dispatch_continuation_init_f(dc,3 + z v a o = / dq, ctxt, func,7 h i 0, dt % Rc_flags);
_dis) 3 T e U -patch_continuation_group_async(dg, dq, dc, qos);
}
调用dispatch_group_enter
进组
static inline void
_dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq,
dispatchf V A 8_contin) } )uation_t dc, dispatch_qos F B { D y z 4 x_t qos)
{
dispatch_grouB * L H ? i Wp_enter(dg);
dc->dc_data = dg;
_dispatch_continC , 9 Muation_async(dq, dc, qos, dc->dc_flags);
}
进 t + %组了之后需求调用出组,也便是履行完使命会出组
在_dispatch_co s 2ntinuatN , % ! 8 o + * Lion_invoke_inline
假如是group
方式就会调用_dispatch_continuation_withs i A 5 _group_invoke
来出组
4.dispatch_group_wait
dispa5 y M H M : R p itch_group_wait与信号量也是异曲O Y | * Z g t同工
dispatch_group_create
创立调度组的时分保存了一个value
- 假如当时
valu6 7 /e
和原始valX @ y 3 J U )ue
相同,表明使命现已全部完结,直接回来0 - 假如
timeout
为 0 也会马上回来,否则调用 _dispatch_groD p h 5up_wait_slow- 在
_dispaB ? $ ( Itch_group_wait_slow
会一向等到使命完结回4 q / d b来 0 - 一向没有完结会回来
timeout
- 在
long
dispatch_. _ Wgroup_wait(dispatch_group_t dg, dispatch_time_t timeout)
{
uint64_t old_state, new_state;
os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, relaxed, {
if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) {
oW 6 x $ ^s_atomic_rmw_loop_give_up_with_fence(acquire, return 0);
}
if (unz N Alikely(timeoK i l E d a 2 K Vut == 0)) {
os_atomic_rmw_loop_give_up(return _DSEMA4_TIMEOUT());
}
new_state = old_state | DISPATCH_GROU= q F _P_HAS_WAITERS;
if (unlikely(old_state & DISPATCH_GROUP_HAS_WAITERS)) {
os_atomic_rmw_loop_; 5 F . 8give_up(break);
}
});
return _dispatch_gro/ + zup_wait_slow(dg, _dg_state_gen, F = h D @(n( ) 2 Few_state), timeout);
}
5.disp} g 4atch_group_notify
等候_dispc . J Yatch_group_wake
回调(全部出组会调用)
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_continuation_t dsn)
{
uint64_t old_state, new_state;
dispatch_continuation_t prev;
dsn->dc_data = dq;
_dispatch_retain(dq);
prev = os_mpsc__ # 6 q H { A 6push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
if (os_mpsc_push_was_empty(prev)) _dispatch_rem v E c ? ` 7tain(dg);
os_mpsc_push_update_prev(os_mpsc(dg,k j ? dg_noU r Ltify), prev, dsn, do_next);
i) E d Wf (os_mpsc_push_was_empty(prev)) {
os_atomic_rmw_4 q Bloop2o(dg, dg_state, old} | D_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_wa0 P w V , , ,ke(dg, new_state, fal$ V . R Y 3 2 ?se);
});
}
});
}
}
static void
_dispatch_group_wake(dispatch_group_t dg, uint64_t dg_state, bool needs_release)
{
uint16_t refs = needs_release ? 1 : 0; // <E n ?rdar://problem/22318411>
if (dg_state & DISPATCH_GROUP_HAS_NOTIFS) {
dispatch_continuation_t dc9 E c - m +, next_dc, tail;
// Snapshot before anything is notified/wV i 9 p Q # Koken
dc = os_mpsc_capture_snapshot(os_ = r v ] u_mpsc(dg, dg_notify), &tail);
do {
dispatch_queue_t dsn_q. k R wueue = (dispatch_queue_t)dc->dc_data;
next_dc = os_mpsc_pop_snapshot_head(dc, tail, do_next* n I n 5 T f ? 1) x m K |;
_dispatch_contin_ . ` ? t H e [uation_async(dsn_queue7 3 e {, dc,
_dispatch_qos_from_pp(dc->dc_@ 1 i - 0priority), dc->dc_flags);
_dispatch_release(dsn_queue);
} while ((dc = next_dc));
refs++;
}
if (dg_state & DISPATCH_GROUP_HAS| W 8 l ! + Y H -_WAITERS) {
_dispatch_wake_by_addro P t p + kess(&dg->dg_gen);
}
if (refs) _dispatch_release_n(dg, refs);
}
七、单例的原理
#define DLOCK_ONCE_UNLOCKED ((ui| ] K s Q # } fntp^ Q V u u i # xtr_t)0)
void
dispatch_once_f(dispc t Gatch_once_t *val,{ Q Z Z void *ctxt, dispatch_function_t func)
{
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
#? n U : & c Rif !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
uintptr_t v = os_atomic_l( c n 2oad(&l->dgo_once, acquire);
if (likely(v == DLOCK_ONCE_f m 0 p P - # x NDONE)) {
return;
}
#if DISPATCH_ONC7 p | %E_USE_QUIESCENT_COUNTER
if (la X 0 g -ikely(DISPATCH_ONCE_IS_GEN(v))) {
return _dispatch_once_mark_done_if_quiesced(l, v);
}
#endif
#V Z R endif
if (_dispatch_once_gate_tryenter(l)) {
return _di- L 9 Aspatch_o! @ Jnce_callout(l, ctxt, func);
}
return _dispatch_once_wa2 = ; N % 2 O }it(l);
}
DISPATCH_ALWAYS_INLINE
static inline bool
_dispao ` { c U V + t Itch_once_gate_tryenter(dispatcb Q eh_once_gate_t l)
{
return os_atomic_cmpxchg(&l->dgo_once, DL- a G r dOCi w + m 1 . # -K_ONCE_UNLOCKED,
(uintptr_t)_dispatch_lock_value_for_self(),! n L 3 ( relaxed);
}
- 第一次调用时外部传进来的
onceToken
为空,所以val
为 NULL-
_dispatch_once_gate_tryenter(l)
判别l->dgo_once
是否标记为DLOCK_ONCE_UNLOCKED
(是否存储过) -
DLOCK_ONCE_UNLOCKED=0
,所以if 判别是建立的,就会进行block
回调 - 再通过
_dispas * | 2 rtch_oncT L | c V ;e_gH B h i C & 0 ? &ate_broadcD t fast
将l->dgo_once
标记为DLOCK_ONCE_DONE
-
- 第2次进来就会直接回来,保证代码只履行一n Z ` [ = { # B次
写在后面
关于上一篇文章中的dispatch_barrier_async
为什么运用大局行列无效能够看浅显易懂 GCD 之 dispatch_queue
GCD源码还真不是一般的不流畅难s 7 & A F w . / D明,笔者水平有限,若有过错之处烦请指出