iOS多线程GCD(二) 源码分析同步函数、异步函数、单例

同步函数

同步函数是在当前线程执行,不会开辟线程,所以就先从同步dispatch_sync开始入手,然后再查看里面队列的区分

dispatch工龄差一年工资差多少_sync

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`,排除`unlikely`,然后就把目标放在
// `_dispatch_sync_f`方法,先来看看参数`_dispatch_Block_invoke`:
#ifdef __BLOCKS__
#define _dispatch_Block_invoke(bb) 
	 ((dispatch_function_t)((struct Block_layout *)bb)->invoke)
// -   这里的`bb`是传入的`work`,也就是`block`,
// 然后`_dispatch_Block_invoke(bb)`就是让`block`调用`invoke`,也就是`block`调用

_dispatch_sync_f

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`就是要研究的`block`,`func`是方法的调用`_dispatch_Block_invoke`

_dispatch_sync_f_inline

static inline void
_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
		dispatch_function_t func, uintptr_t dc_flags)
{
// 当`dq_width`值为`1`时是`串行队列`,
// 也就是这里串行会走`_dispatch_barrier_sync_f`方法
     if (likely(dq->dq_width == 1)) { // 串行
	 return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags); // 栅栏函数
     }
     if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {
	 DISPATCH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");
     }
     dispatch_lane_t dl = upcast(dq)._dl;
     // Global concurrent queues and queues bound to non-dispatch threads
     // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
     if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {
	 return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);  // 先来
     }
     if (unlikely(dq->do_targetq->do_targetq)) {
	 return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
     }
     _dispatch_introspection_sync_begin(dl);
     _dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
	      _dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}

同步函数串行队列

_dispatch_barrier_sync_f

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dispat变量英语ch线程池原理_lane_barrier_sync_invok源码中的图片e_and_complete

正常的同步函数串行队列

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • func是参数,但后面也有个源码时代DISPATCH_TRACE_ARG(void *dc)参数,但参数和参数之间怎么没有,隔开
  • DISPAT源码编辑器下载CH_TRACE_ARG: #define DISPATCH_TRACE_ARG(arg) #宫颈癌define DISPATCH_TRACE_ARG(arg) , arg有参数时,它带上了, 再加参数,没有参数时代表空
_dispatch_sync_function_invoke_inline

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dispatch_client_龚俊callout

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里不管likely(!u)条件是否限制,都会执行f(ctxt),也就是block调用
_dispatch_sync线程池_f变量泵_工商银行s变量泵low

在上面_dispatch_barrier_sync_f_inline方法后,如果是死锁的情况,就会进入_disp变量名的命名规则atch_sync_f_slow方法

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

__DISPATCH_WAIT_FOR_QUEUE__

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dq_state_drainGo_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;
}
#define DLOCK_OWNER_MASK			((dispatch_lock)0xfffffffc)
  • DLOCK_OWNER_MASK是个很大的值变量名,也就是只有lock_value源码中的图片tid相同时,与上公司让员工下班发手机电量截图DLOCK_OWNER_MASK的结果才会为0线程池的七个参数也即是当前队列要等待的和需要执行的队列要等待的一样时,会出现你要等我,我要等你,进而产生死锁

同步函数并行队列

在上面_dispatch_sync_f_inline函数,如果dq_width不为1,就是并发队列,通过符号断点确认走_dispatch_sync源码时代_f_slow 方法里有两处用到了func,分别是_dispatch_sync_function_invoke_dispatch_sync_invoke_and_complete_recurse,再下符号断点确认,最终进入_dispatch_sync_functio变量与函数n_invoke方法

_dispatch_sync_function_invoke

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例
_dispatch_sync_function_invoke_inline方法,和串行队列一样

_dispatch_sync_funct安全教育平台作业登录ion_invoke_inl变量之间的关系in变量e

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dispatch_client_callout

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 最后通过f(ctxt)进行调用

流程图总结

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

异步函数

dispatch_async

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dispatch_continuation_alloc

static inline dispatch_continuation_t
_dispatch_continuation_alloc(void)
{
     dispatch_continuation_t dc =
	       _dispatch_continuation_alloc_cacheonly(); // 先从线程池获取线程可执行任务的线程
     if (unlikely(!dc)) { // 如果没有,就从堆中开辟一个空间创建一条
	 return _dispatch_continuation_alloc_from_heap(); 
     }
     return dc;
}
_dispatch_continuation_alloc_cacheonl变量与函数y
static inline dispatch_continuation_t
_dispatch_continuation_alloc_cacheonly(void)
{
     dispatch_continuation_t dc = (dispatch_continuation_t)
	       _dispatch_thread_getspecific(dispatch_cache_key);
     if (likely(dc)) {
	 _dispatch_thread_setspecific(dispatch_cache_key, dc->do_next);
     }
     return dc;
}
  • 根据key获取可执行的线程,如果获取到就调用dc->do_next给他一个可执行下个任务的状态,set和get方法如下:

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 没有找到线程线程池有哪几种就执行_dispatch_con线程池原理tinuation_alloc_from_heap方法
_dispatch_continuation_alloc_from_heap

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dispatch_continuation_async

  • 系统调用_dispatch_con线程池的七个参数tinuation_init函数,将blockdq(队列)dc(线程)生成一个dispatch_qos_t源码之家型的qos,然后线程池的工作原理调用_dispatch_continuation_async
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
		dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
//`block`调用,也就是`dqu`相关的
#if DISPATCH_INTROSPECTION
     if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
	 _dispatch_trace_item_push(dqu, dc);
     }
#else
     (void)dc_flags;
#endif
     return dx_push(dqu._dq, dc, qos); 
     // #define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
}

block调用,也就是dqu相关的,所以就看dq_push

异步主队列_dispatch_main_queue_push

异步主队源码交易平台列的dp_p线程池的工作原理ush类型是_dispatch_main_queue_push

void
_dispatch_main_queue_push(dispatch_queue_main_t dq, dispatch_object_t dou,
		dispatch_qos_t qos)
{
     // Same as _dispatch_lane_push() but without the refcounting due to being
     // a global object
     if (_dispatch_queue_push_item(dq, dou)) {
	 return dx_wakeup(dq, qos, DISPATCH_WAKEUP_MAKE_DIRTY);
     }
     qos = _dispatch_queue_push_qos(dq, qos);
     if (_dispatch_queue_need_override(dq, qos)) {
	 return dx_wakeup(dq, qos, 0);
         // #define dx_wakeup(x, y, z) dx_vtable(x)->dq_wakeup(x, y, z)
     }
}

dq_wakeup,选择主队列的赋值_dispatch_ma安全教育平台登录in_queue_wakeup,它的实现如下:

_dispatch_main_queue_wak线程池原理eup
void
_dispatch_main_queue_wakeup(dispatch_queue_main_t dq, dispatch_qos_t qos,
		dispatch_wakeup_flags_t flags)
{
#if DISPATCH_COCOA_COMPAT
     if (_dispatch_queue_is_thread_bound(dq)) {
	 return _dispatch_runloop_queue_wakeup(dq->_as_dl, qos, flags);
     }
#endif
     return _dispatch_lane_wakeup(dq, qos, flags);
}
_dispatch_runloop_queue_wakeup
void
_dispatch_runloop_queue_wakeup(dispatch_lane_t dq, dispatch_qos_t qos,
	  dispatch_wakeup_flags_t flags)
{
     if (unlikely(_dispatch_queue_atomic_flags(dq) & DQF_RELEASED)) {
	 // <rdar://problem/14026816>
	 return _dispatch_lane_wakeup(dq, qos, flags);
     }
     if (flags & DISPATCH_WAKEUP_MAKE_DIRTY) {
	 os_atomic_or2o(dq, dq_state, DISPATCH_QUEUE_DIRTY, release);
     }
     if (_dispatch_queue_class_probe(dq)) {
	 return _dispatch_runloop_queue_poke(dq, qos, flags);
     }
     qos = _dispatch_runloop_queue_reset_max_qos(dq);
     if (qos) {
	 mach_port_t owner = DISPATCH_QUEUE_DRAIN_OWNER(dq);
	 if (_dispatch_queue_class_probe(dq)) {
	     _dispatch_runloop_queue_poke(dq, qos, flags);
	 }
	 _dispatch_thread_override_end(owner, dq);
	 return;
     }
     if (flags & DISPATCH_WAKEUP_CONSUME_2) {
	 return _dispatch_release_2_tailcall(dq);
     }
}
_dispatch安全生产法_runloop_queue_poke
static void
_dispatch_runloop_queue_poke(dispatch_lane_t dq, dispatch_qos_t qos,
		dispatch_wakeup_flags_t flags)
{
     // it's not useful to handle WAKEUP_MAKE_DIRTY because mach_msg() will have
     // a release barrier and that when runloop queues stop being thread-bound
     // they have a non optional wake-up to start being a "normal" queue
     // either in _dispatch_runloop_queue_xref_dispose,
     // or in _dispatch_queue_cleanup2() for the main thread.
     uint64_t old_state, new_state;
     if (dx_type(dq) == DISPATCH_QUEUE_MAIN_TYPE) {
	 dispatch_once_f(&_dispatch_main_q_handle_pred, dq,
				_dispatch_runloop_queue_handle_init);
     }
     ...
}
  • 这里就能看到主线程的判断,dispatch_once_f是一个单例方法,而第三个参数_dispatch_runloop_queue_handle_init就是要回调的方法,实现如下:

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dispatc源码精灵永久兑换码h_runloop_root_queue_安全教育日create_4CF

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dis变量值patch_main_queue_callback_4CF

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 然后在单例方法dispat安全生产法ch_on安全工程师ce_f中找到_dispatch_once_callout进行回调
_dispatch_once_callout

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 最后走到了_dispatch_client_callout函数,进而进行block回调

异步串行队列_di公司让员工下班发手机电量截图spatch_lane_push

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里会走到dx_wakeup->dq_wakeup中的_dispatch_lane_wakeup类型
_dispatch_lane_wakeup

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 然后会进入_dispatch_queue_wakeup方法
_dispatch_queue_wakeup

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里第一个参数已经改变,然后会进入_dispatch_queue_push_queue方法
_dispatch_qu源码之家eue_push_queue

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 根据反推法得知会进入_dispatch_event_loop_poke方法
_dispatch_event_loop_poke

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里会进入_dispatch_trace_item_push方法
_dispatch_trace_item_push

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 根据反推法会进入_dispatch_trace_continuation方法
_dispatch_trace_continuation

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这是个宏定义,我们只需要找到相关func就可以,也就是_dispatch_lane_invoke
_dispatch_lane_invoke

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 由于_dispatch_queue_class_invoke是根据第线程池拒绝策略五个参数invoke来获取tq,所以我们只需研究变量类型有哪些_dispatch_la宫颈癌ne_invoke2
_dispatch工商银行电话人工客服_lane_invok枸杞e2

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 根据前面我们得知dq_w源码之家idth = 1时是串行,所以我们将目标放在_dispatch_lan变量名的命名规则e_serial_drain函数
_dispatch_lane_serial_drain

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 在查看里面方法_dispatch_lane_drain
_dispatch_lane_drain

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 经过分析会走到_dis工龄差一年工资差多少patch_continuationgoogle_pop_inline方法
_disp安全教育日atch宫颈癌_c安全生产法onti线程池原理nuatio安全模式怎么解除n_pop_inline

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 在该函数里最终会走进_dispatch_continuation_invoke_inline方法
_dispatch_continuat源码ion_invoke_inline

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 最终在里面找到了_disp工龄越长退休金越多吗atch_client_callout函数

异步全局队列_dispatch_root_queue_push

源码如下:

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 根据分析,代码会走_dispatch_root_queue_pu变量泵sh_inline方法
_dispa变量英语tch_root_源码精灵永久兑换码queue_push_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);
     }
}
复制代码
  • 然后会走_dispatch_root_queue_poke方法
_dispatch_root_queue_poke

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 然后来到_dispatch_root_queue_poke_slow方法:
_dispatch_root安全_queue_poke_slow
  • 这里面的主要调用相关方法比较隐蔽,在_dispatch_root_queues_init里面,它的实现如下:
_dispatch_ro安全教育手抄报ot_queues_i变量是什么意思nit
static inline void
_dispatch_root_queues_init(void)
{
     dispatch_once_f(&_dispatch_root_queues_pred, NULL,
	      _dispatch_root_queues_init_once);
}
复制代码
  • 这是个龚俊单例下面再讲它的原理,主要的回调函数封装在_dispat安全教育平台登录ch_root_queues_init_once
_dispatch_root_queues_init_once

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里主要是创建一个pthread实例,然后将安全教育平台登录任务_dispatch_worker_thread2交给它处理
_dispatch_work安全工程师er_thread2

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里主要是获安全工程师root_queue,然后再调用_dispatch_root_queue_d线程池拒绝策略rain线程池的工作原理方法
_dispatch_roo变量英语t_queue_drain

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 然后再进入_dispatch_continuati安全教育手抄报on_pop_i变量名nline方法
_dispat安全期计算器ch_continuation_po安全教育平台登录入口p_inline

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 根据反推法,得知这里走了dx_invoke,根据dx_invoke找到do_in源码1688voke

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

_dispatch_async_redirect_invoke

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 然后会进入_dispatch_continuation_pop方法
_dispatch_continuation_pop

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 方法里又回到_dispatch_continuation_pop_inline函数,此时进入函数会执行_dispatch_continuation_invoke_inline方法
_dispatch_continuation_invoke_inline

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 于是就找到了_dispatch_client_callout函数,也就线程池的七个参数源码中的图片最终的回调函数处
线程处理

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里的do-while是处理线程的安全教育日开辟,首先需要拿到remaining的值为1,然后和can_request做对比,如果remaining大于can_request,则把can_request的值给remaining防止出错,当remaining0时,说明线程池已经满了
  • dgq_thread_pool_size是线程池的大小,根据搜索发现初始值为1
最大并发数

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 搜索发现dgq_threa线程池的使用d_pool_size的值最安全工程师大为DISPATCH_WORKQ_MAX_PTHREAD_COUNT
#define DISPATCH_WORKQ_MAX_PTHREAD_COUNT 255
复制代码

结论:线程池中理论最大的并发数是255

源码占用的内存是多少呢?

占用内存

根据Thread Costs中相关说明google,辅助线程最小堆栈大小为16KB ,并且大小必须是4KB的倍数

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 如果在一定的内存内,如果创建的线程内存越大,变量的定义则能开辟的线程越少

源码编辑器步并发队列_dispatch_源码1688lane_concurrent_push

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 根据符号断点得知会执行_dispatch_continuation_redirect_线程池拒绝策略push方法
_dispatch_continuation_redirect_push

iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

  • 这里又进入线程池的工作原理dx_push方法,但此时dq已经变了,会走线程池有哪几种_dispatch_root_queue_push,之后的流程和异步全局队列一样.

单例

dispatch_once

void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
    dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
复制代码
  • 底层只有控制变量与函数参数val和回调方法block,原理就线程池的使用是通过改变变量来达到安全工程师核心方法只执行一次线程池核心参数的效安全生产法果。

dispatch_once_f源码编辑器

  • 再来看看dispatch_once_f函数:
void
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);
}
复制代码
  • 这里先将val强转成dispatch_once_gate_t类型的l

  • 然后再根据&l->dgo_once判断是否工商银行已经执行过一次,如果已经执行过了,就直接返回

  • 如果安全教育日没用执行过就来到_dispatch_once_gate_tryente变量泵r:

    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); // 原子操作加锁
    }
    复制代码
    
    • 该方法是对自己进行加锁,并判断是否成功

_di变量之间的关系spatch_clie工龄越长退休金越多吗nt_callout

  • 如果源码网站加锁成功了,则执行_dispa源码精灵永久兑换码tch_once_callout函数:

    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_client_callout是进行函数回调
    • _源码1688dispatch_once_gate_broadcast函数是进行标记:

    iOS多线程GCD(二)  源码分析同步函数、异步函数、单例

    • 这里_dispatch_once_mark_done的实现如下:
    static inline uintptr_t
    _dispatch_once_mark_done(dispatch_once_gate_t dgo)
    {
         return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release); // 将状态标记成 DLOCK_ONCE_DONE
    }
    复制代码
    
    • _dispatch_once_mark_do变量之间的关系ne方法主要是将状态标记成DLOCK_ONCE_DONE,等下次执行单例时方便判断

_dispatch_once_wait

  • 如果没用执行,然后也被锁锁住了,就会执行_dispatch_once_wait方法进行等待,等待开锁

发表评论

提供最优质的资源集合

立即查看 了解详情