網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
引言
在上一篇文章中,我們探尋了隊(duì)列是怎么創(chuàng)建的,串行隊(duì)列和并發(fā)隊(duì)列之間的區(qū)別,接下來(lái)我們?cè)谔綄ひ幌翯CD的另一個(gè)核心 - 任務(wù)
同步任務(wù)
void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
我們先通過(guò)lldb查看其堆棧信息,分別查看其正常運(yùn)行和死鎖狀態(tài)的信息
我們?cè)偻ㄟ^(guò)源碼查詢其實(shí)現(xiàn)
#define _dispatch_Block_invoke(bb) ((dispatch_function_t)((struct Block_layout *)bb)->invoke)
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);
}
其通過(guò)_dispatch_Block_invoke將函數(shù)包裝成了一個(gè)block,后繼續(xù)向下傳遞,也就是說(shuō)我們的代碼是通過(guò)這個(gè)block來(lái)進(jìn)行的執(zhí)行,繼續(xù)查詢其的傳遞
static inline void _dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt, dispatch_function_t func, uintptr_t dc_flags) {
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)));
}
我們發(fā)現(xiàn)_dispatch_sync_f_inline中存在我們查看的堆棧信息時(shí)的兩個(gè)函數(shù)_dispatch_sync_f_slow、_dispatch_sync_invoke_and_complete
static void _dispatch_sync_f_slow(dispatch_queue_class_t top_dqu, void *ctxt, dispatch_function_t func, uintptr_t top_dc_flags, dispatch_queue_class_t dqu, uintptr_t dc_flags) {
...省略部分...
struct dispatch_sync_context_s dsc = {
...省略部分...
.dsc_func = func,
};
__DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);
...省略部分...
_dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags DISPATCH_TRACE_ARG(&dsc));
}
static void _dispatch_sync_invoke_and_complete_recurse(dispatch_queue_class_t dq, void *ctxt, dispatch_function_t func, uintptr_t dc_flags DISPATCH_TRACE_ARG(void *dc)) {
_dispatch_sync_function_invoke_inline(dq, ctxt, func);
_dispatch_trace_item_complete(dc);
_dispatch_sync_complete_recurse(dq._dq, NULL, dc_flags);
}
static void _dispatch_sync_invoke_and_complete(dispatch_lane_t dq, void *ctxt, dispatch_function_t func DISPATCH_TRACE_ARG(void *dc)) {
_dispatch_sync_function_invoke_inline(dq, ctxt, func);
_dispatch_trace_item_complete(dc);
_dispatch_lane_non_barrier_complete(dq, 0);
}
我們發(fā)現(xiàn),兩種堆棧信息的函數(shù)的func這個(gè)block都是傳遞給的_dispatch_sync_function_invoke_inline
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);
}
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); // 調(diào)用block函數(shù),執(zhí)行任務(wù)
_dispatch_free_unwind_tsd();
_dispatch_set_unwind_tsd(u);
}
最終通過(guò)_dispatch_client_callout函數(shù)執(zhí)行的我們的block代碼,和我們正常執(zhí)行的堆棧信息相符合。
通過(guò)我們的源碼的探索,我們發(fā)現(xiàn)了在同步任務(wù)中并沒(méi)有任何關(guān)于開(kāi)辟線程的操作,而且任務(wù)并沒(méi)有保存而是直接執(zhí)行的。
死鎖
我們?cè)讷@取的堆棧信息發(fā)現(xiàn)了崩潰調(diào)用的函數(shù)是__DISPATCH_WAIT_FOR_QUEUE__,在源碼中查看
static void __DISPATCH_WAIT_FOR_QUEUE__(dispatch_sync_context_t dsc, dispatch_queue_t dq) {
uint64_t dq_state = _dispatch_wait_prepare(dq);
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"); // 當(dāng)前線程已存在這個(gè)同步隊(duì)列
}
...省略部分...
}
// crash條件
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
// lock_value 當(dāng)前隊(duì)列, tid 當(dāng)前線程
return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}
通過(guò)這里可以看到 崩潰的條件:串行隊(duì)列,當(dāng)前隊(duì)列已在當(dāng)前線程中。
異步任務(wù)
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
一樣先通過(guò)lldb查看一下堆棧信息
很明顯的和同步任務(wù)的區(qū)別是里面有pthread.dylib的調(diào)用,我們還是來(lái)通過(guò)源碼看一下吧。
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);
}
static inline dispatch_qos_t _dispatch_continuation_init(dispatch_continuation_t dc, dispatch_queue_class_t dqu, dispatch_block_t work, dispatch_block_flags_t flags, uintptr_t dc_flags) {
void *ctxt = _dispatch_Block_copy(work);
...省略部分...
dispatch_function_t func = _dispatch_Block_invoke(work);
}
static inline dispatch_qos_t _dispatch_continuation_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_flags) {
pthread_priority_t pp = 0;
dc->dc_flags = dc_flags | DC_FLAG_ALLOCATED;
dc->dc_func = f;
dc->dc_ctxt = ctxt;
……
_dispatch_continuation_voucher_set(dc, flags);
return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
}
異步任務(wù)將任務(wù)先用_dispatch_continuation_init進(jìn)行了copy操作,保存了任務(wù),同時(shí)和同步函數(shù)一樣將任務(wù)用_dispatch_Block_invoke進(jìn)行了封裝,然后將copy的任務(wù)和封裝的block賦值給dispatch_continuation_t dc,也就是相當(dāng)于保存了隊(duì)列中添加的任務(wù),最終返回一個(gè)dispatch_qos_t的對(duì)象qos。
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
static inline void _dispatch_continuation_async(dispatch_queue_class_t dqu, dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags) {
return dx_push(dqu._dq, dc, qos);
}
源碼中全局搜索dq_push,我們?cè)谑煜さ奈募﨑ispatch Source/init.c中找到了每種隊(duì)列對(duì)應(yīng)的dq_push
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
.do_type = DISPATCH_QUEUE_SERIAL_TYPE,
......
.dq_push = _dispatch_lane_push,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
.do_type = DISPATCH_QUEUE_CONCURRENT_TYPE,
......
.dq_push = _dispatch_lane_concurrent_push,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
.do_type = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
......
.dq_push = _dispatch_root_queue_push,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
.do_type = DISPATCH_QUEUE_MAIN_TYPE,
......
.dq_push = _dispatch_main_queue_push,
);
我們通過(guò)最常用的global的_dispatch_root_queue_push來(lái)進(jìn)行探索
void _dispatch_root_queue_push(dispatch_queue_global_t rq, dispatch_object_t dou, dispatch_qos_t qos) {
...省略部分...
#if HAVE_PTHREAD_WORKQUEUE_QOS
if (_dispatch_root_queue_push_needs_override(rq, qos)) {
return _dispatch_root_queue_push_override(rq, dou, qos);
}
#else
(void)qos;
#endif
_dispatch_root_queue_push_inline(rq, dou, dou, 1);
}
static void _dispatch_root_queue_push_override(dispatch_queue_global_t orig_rq, dispatch_object_t dou, dispatch_qos_t qos) {
......
_dispatch_root_queue_push_inline(rq, dc, dc, 1);
}
我們可以看到其內(nèi)部是調(diào)用的_dispatch_root_queue_push_inline函數(shù),進(jìn)一步說(shuō)調(diào)用_dispatch_root_queue_poke_slow
static void _dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor) {
......
_dispatch_root_queues_init();
...利用線程池調(diào)度任務(wù)等相關(guān)代碼...
}
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中進(jìn)行了線程對(duì)任務(wù)的調(diào)度
_dispatch_worker_thread2, _dispatch_worker_thread2 ->
_dispatch_root_queue_drain -> _dispatch_continuation_pop_inline ->
_dispatch_continuation_invoke_inline,_dispatch_root_queue_poke_slow
進(jìn)行了線程池的相關(guān)操作
也就是我們?cè)诙褩P畔⒅衟thread.dylib的調(diào)用的原因。這些異步調(diào)度我們已經(jīng)無(wú)法進(jìn)行下一步查看了,所以還是看回我們的堆棧信息,很明顯函數(shù)的執(zhí)行仍是通過(guò)_dispatch_client_callout進(jìn)行的執(zhí)行。
總結(jié)
- 同步任務(wù):任務(wù)立即執(zhí)行,無(wú)線程相關(guān)操作,會(huì)阻塞當(dāng)前線程
- 異步任務(wù):保存任務(wù),進(jìn)行線程相關(guān)操作,可以開(kāi)辟子線程,不會(huì)阻塞當(dāng)前線程
- 死鎖:在當(dāng)前線程同步(和當(dāng)前隊(duì)列相關(guān))同步的向里面添加任務(wù),就會(huì)死鎖
原文鏈接:https://juejin.cn/post/7101595848580005924
相關(guān)推薦
- 2022-08-11 C++簡(jiǎn)明講解缺省參數(shù)與函數(shù)重載的用法_C 語(yǔ)言
- 2022-12-25 一文帶你了解Go語(yǔ)言中的指針和結(jié)構(gòu)體_Golang
- 2023-01-14 GoLang內(nèi)存模型詳細(xì)講解_Golang
- 2022-07-02 python等間距取值方式_python
- 2022-04-08 WPF引用MVVM框架與使用方法_基礎(chǔ)應(yīng)用
- 2022-04-23 如何在String原型上封裝一個(gè)時(shí)間戳轉(zhuǎn)日期的方法詳解
- 2023-10-09 mobx中react的觀察者
- 2022-09-12 docker?清理緩存腳本解析_docker
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支