同步函数
同步函数是在当前线程执行,不会开辟线程,所以就先从同步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
_dispat变量英语ch线程池原理_lane_barrier_sync_invok源码中的图片e_and_complete
正常的同步函数串行队列
-
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
_dispatch_client_龚俊callout
- 这里不管
likely(!u)
条件是否限制,都会执行f(ctxt)
,也就是block
调用
_dispatch_sync线程池_f变量泵_工商银行s变量泵low
在上面_dispatch_barrier_sync_f_inline
方法后,如果是死锁的情况,就会进入_disp变量名的命名规则atch_sync_f_slow
方法
__DISPATCH_WAIT_FOR_QUEUE__
_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
_dispatch_sync_function_invoke_inline
方法,和串行队列一样
_dispatch_sync_funct安全教育平台作业登录ion_invoke_inl变量之间的关系in变量e
_dispatch_client_callout
- 最后通过
f(ctxt)
进行调用
流程图总结
异步函数
dispatch_async
_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
方法如下:
- 没有找到线程线程池有哪几种就执行
_dispatch_con线程池原理tinuation_alloc_from_heap
方法
_dispatch_continuation_alloc_from_heap
_dispatch_continuation_async
- 系统调用
_dispatch_con线程池的七个参数tinuation_init
函数,将block
,dq(队列)
和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
就是要回调的方法,实现如下:
_dispatc源码精灵永久兑换码h_runloop_root_queue_安全教育日create_4CF
_dis变量值patch_main_queue_callback_4CF
- 然后在单例方法
dispat安全生产法ch_on安全工程师ce_f
中找到_dispatch_once_callout
进行回调
_dispatch_once_callout
- 最后走到了
_dispatch_client_callout
函数,进而进行block
回调
异步串行队列_di公司让员工下班发手机电量截图spatch_lane_push
- 这里会走到
dx_wakeup->dq_wakeup
中的_dispatch_lane_wakeup
类型
_dispatch_lane_wakeup
- 然后会进入
_dispatch_queue_wakeup
方法
_dispatch_queue_wakeup
- 这里第一个参数已经改变,然后会进入
_dispatch_queue_push_queue
方法
_dispatch_qu源码之家eue_push_queue
- 根据反推法得知会进入
_dispatch_event_loop_poke
方法
_dispatch_event_loop_poke
- 这里会进入
_dispatch_trace_item_push
方法
_dispatch_trace_item_push
- 根据反推法会进入
_dispatch_trace_continuation
方法
_dispatch_trace_continuation
- 这是个宏定义,我们只需要找到相关
func
就可以,也就是_dispatch_lane_invoke
_dispatch_lane_invoke
- 由于
_dispatch_queue_class_invoke
是根据第线程池拒绝策略五个参数invoke
来获取tq
,所以我们只需研究变量类型有哪些_dispatch_la宫颈癌ne_invoke2
_dispatch工商银行电话人工客服_lane_invok枸杞e2
- 根据前面我们得知
dq_w源码之家idth = 1
时是串行,所以我们将目标放在_dispatch_lan变量名的命名规则e_serial_drain
函数
_dispatch_lane_serial_drain
- 在查看里面方法
_dispatch_lane_drain
_dispatch_lane_drain
- 经过分析会走到
_dis工龄差一年工资差多少patch_continuationgoogle_pop_inline
方法
_disp安全教育日atch宫颈癌_c安全生产法onti线程池原理nuatio安全模式怎么解除n_pop_inline
- 在该函数里最终会走进
_dispatch_continuation_invoke_inline
方法
_dispatch_continuat源码ion_invoke_inline
- 最终在里面找到了
_disp工龄越长退休金越多吗atch_client_callout
函数
异步全局队列_dispatch_root_queue_push
源码如下:
- 根据分析,代码会走
_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
- 然后来到
_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
- 这里主要是创建一个
pthread
实例,然后将安全教育平台登录任务_dispatch_worker_thread2
交给它处理
_dispatch_work安全工程师er_thread2
- 这里主要是获安全工程师取
root_queue
,然后再调用_dispatch_root_queue_d线程池拒绝策略rain线程池的工作原理
方法
_dispatch_roo变量英语t_queue_drain
- 然后再进入
_dispatch_continuati安全教育手抄报on_pop_i变量名nline
方法
_dispat安全期计算器ch_continuation_po安全教育平台登录入口p_inline
- 根据反推法,得知这里走了
dx_invoke
,根据dx_invoke
找到do_in源码1688voke
_dispatch_async_redirect_invoke
- 然后会进入
_dispatch_continuation_pop
方法
_dispatch_continuation_pop
- 方法里又回到
_dispatch_continuation_pop_inline
函数,此时进入函数会执行_dispatch_continuation_invoke_inline
方法
_dispatch_continuation_invoke_inline
- 于是就找到了
_dispatch_client_callout
函数,也就线程池的七个参数是源码中的图片最终的回调函数处
线程处理
- 这里的
do-while
是处理线程的安全教育日开辟,首先需要拿到remaining
的值为1
,然后和can_request
做对比,如果remaining
大于can_request
,则把can_request
的值给remaining
防止出错,当remaining
为0
时,说明线程池已经满了 -
dgq_thread_pool_size
是线程池的大小,根据搜索发现初始值为1
最大并发数
- 搜索发现
dgq_threa线程池的使用d_pool_size
的值最安全工程师大为DISPATCH_WORKQ_MAX_PTHREAD_COUNT
#define DISPATCH_WORKQ_MAX_PTHREAD_COUNT 255
复制代码
结论:线程池中理论最大的并发数是
255
但源码占用的内存是多少呢?
占用内存
根据Thread Costs中相关说明google,辅助线程最小堆栈大小为16KB
,并且大小必须是4KB
的倍数
- 如果在一定的内存内,如果创建的线程内存越大,变量的定义则能开辟的线程越少
异源码编辑器步并发队列_dispatch_源码1688lane_concurrent_push
- 根据符号断点得知会执行
_dispatch_continuation_redirect_线程池拒绝策略push
方法
_dispatch_continuation_redirect_push
- 这里又进入线程池的工作原理了
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
函数是进行标记:
- 这里
_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
方法进行等待,等待开锁