libdispatch.dylib
源碼地址https://opensource.apple.com/release/macos-1015.html
隊列創建
- 在源碼中搜索
dispatch_queue_create
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(
并進入
DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
if (!slowpath(dqa)) { // 如果是串行隊列
dqa = _dispatch_get_default_queue_attr(); // 串行隊列的attr 取默認attr
} else if (dqa->do_vtable != DISPATCH_VTABLE(queue_attr)) { // 并行隊列的 attr->do_vtable 應該等于 DISPATCH_VTABLE(queue_attr)
DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
}
// dqai 創建 -
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
//第一步:規范化參數,例如qos, overcommit, tq
// dispatch_qos_t qos是優先級?
dispatch_qos_t qos = _dispatch_priority_qos(dqa->dqa_qos_and_relpri);
// 是否overcommit(即queue創建的線程數是否允許超過實際的CPU個數)
_dispatch_queue_attr_overcommit_t overcommit = dqa->dqa_overcommit;
if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) { //
if (tq->do_targetq) { // overcommit 的queue 必須是全局的
DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
"a non-global target queue");
}
}
// 下面這些代碼,因為用戶創建的queue的tq一定為NULL,因此,只要關注tq == NULL的分支即可,我們刪除了其余分支
if (!tq) { // 自己創建的queue,tq都是null
tq = _dispatch_get_root_queue( // 在root queue里面去取一個合適的queue當做target queue
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 無論是用戶創建的串行還是并行隊列,其qos都沒有指定,因此,qos這里都取DISPATCH_QOS_DEFAULT
overcommit == _dispatch_queue_attr_overcommit_enabled);
if (slowpath(!tq)) { // 如果根據create queue是傳入的屬性無法獲取到對應的tq,crash
DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
}
}
...
//拼接隊列名稱
// 根據不同的queue類型,設置vtable。vtable實現了SERIAL queue 和 CONCURRENT queue的行為差異。
const void *vtable;
dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
if (dqai.dqai_concurrent) { // 并發
// OS_dispatch_queue_concurrent
vtable = DISPATCH_VTABLE(queue_concurrent);
} else {// 串行
vtable = DISPATCH_VTABLE(queue_serial);
}
....
//創建隊列,并初始化
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s)); // alloc
//根據dqai.dqai_concurrent的值,就能判斷隊列 是 串行 還是并發,對于并發隊列,其queue width是DISPATCH_QUEUE_WIDTH_MAX,而串行隊列其width是1
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
//設置隊列label標識符
dq->dq_label = label;//設置dq的名字
dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos, dqai.dqai_relpri);//優先級處理
...
//類似于類與元類的綁定,不是直接的繼承關系,而是類似于模型與模板的關系
dq->do_targetq = tq;
_dispatch_object_debug(dq, "%s", __func__);
return _dispatch_trace_queue_create(dq)._dq;//研究dq
}
_dispatch_lane_create_with_target源碼分析
-【第一步】_dispatch_queue_attr_to_info
方法傳入daq(隊列類型)
創建_dispatch_queue_attr_to_info_t
類型的對象dqai
,用于存儲隊列的相關屬性信息
-【第二步】設置隊列的相關屬性
,例如服務質量qos
、是否overcommit
(隊列創建的線程數是否允許超過實際的CPU個數
)、tq
(自己創建的隊列tq一定為NULL
)
-【第三步】DISPATCH_VTABLE
宏定義拼接隊列名字,OS_dispatch_
+隊列類型
=隊列名字
- 串行隊列:OS_dispatch_queue_serial
- 并發隊列:OS_dispatch_queue_concurrent
#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS(name) OS_dispatch_##name
-【第四步】初始化隊列dq
(alloc init),dqai.dqai_concurrent
可以判斷隊列類型,DISPATCH_QUEUE_WIDTH_MAX==并發,1==串行
- 進入_dispatch_object_alloc -> _os_object_alloc_realized
方法中,發現里面設置了isa
指向,說明了隊列是對象
- 進入
_dispatch_queue_init
方法,初始化隊列相關屬性-【第五步】通過_dispatch_trace_queue_create
處理創建好的dq
隊列
- 進入
_dispatch_introspection_queue_create_hook -> dispatch_introspection_queue_get_info -> _dispatch_introspection_lane_get_info
方法,實現了一個模板隊列
總結
-
dispatch_queue_create
中隊列類型決定了下層dq_width
的值(max=并發,1=串行
) -
隊列queue
是對象
,通過alloc+init創建,在alloc中有個class(宏定義)
指定isa的指向
-
隊列
在底層是通過模板創建的,類型是結構體dispatch_introspection_queue_s
隊列創建分析
異步函數
進入dispatch_async
的源碼實現
-
_dispatch_continuation_init
:任務包裝函數 -
_dispatch_continuation_async
:并發處理函數
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)//work 任務
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME;
dispatch_qos_t qos;
// 任務包裝器(work在這里才有使用) - 接受work - 保存work - 并函數式編程
// 保存 block
qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
//并發處理
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
_dispatch_continuation_init任務包裝
DISPATCH_ALWAYS_INLINE
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);//拷貝任務
dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
if (unlikely(_dispatch_block_has_private_data(work))) {
dc->dc_flags = dc_flags;
dc->dc_ctxt = ctxt;//賦值
// will initialize all fields but requires dc_flags & dc_ctxt to be set
return _dispatch_continuation_init_slow(dc, dqu, flags);
}
dispatch_function_t func = _dispatch_Block_invoke(work);//封裝work - 異步回調
if (dc_flags & DC_FLAG_CONSUME) {
func = _dispatch_call_block_and_release;//回調函數賦值 - 同步回調
}
return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}
- 通過
_dispatch_Block_copy
拷貝任務 - 通過
_dispatch_Block_invoke
封裝任務,是一個異步回調函數 - 如果是
同步
,則回調函數賦值為_dispatch_call_block_and_release
- 通過
_dispatch_continuation_init_f
方法賦值,將func任務
保存在屬性中
_dispatch_continuation_init_f
_dispatch_continuation_async并發處理
主要是執行block回調
,進入_dispatch_continuation_async
源碼
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#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);//與dx_invoke一樣,都是宏
}
- 關鍵代碼
dx_push
是個宏
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
-
dx_push
根據隊列類型執行不同的函數
dq_push -
進入
_dispatch_root_queue_push -> _dispatch_root_queue_push_inline ->_dispatch_root_queue_poke -> _dispatch_root_queue_poke_slow
源碼,- 通過
_dispatch_root_queues_init
注冊回調 - 使用
pthread_create
方法通過do-while
循環創建線程
- 通過
DISPATCH_NOINLINE
static void
_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)
{
int remaining = n;
int r = ENOSYS;
_dispatch_root_queues_init();//重點
...
//do-while循環創建線程
do {
_dispatch_retain(dq); // released in _dispatch_worker_thread
while ((r = pthread_create(pthr, attr, _dispatch_worker_thread, dq))) {
if (r != EAGAIN) {
(void)dispatch_assume_zero(r);
}
_dispatch_temporary_resource_shortage();
}
} while (--remaining);
...
}
_dispatch_root_queues_init
通過dispatch_once_f
單例實現
DISPATCH_ALWAYS_INLINE
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
_dispatch_root_queues_init_once -
block的回調執行路徑是
_dispatch_root_queues_init_once ->_dispatch_worker_thread2 -> _dispatch_root_queue_drain -> _dispatch_root_queue_drain -> _dispatch_continuation_pop_inline -> _dispatch_continuation_invoke_inline -> _dispatch_client_callout -> dispatch_call_block_and_release
,可以通過bt
打印堆棧信息,
bt打印堆棧
總結
- 將異步任務拷貝并封裝后,設置回調函數
func
- 通過
dx_push遞歸
,重定向到跟隊列
,通過pthread_creat
方法do-while
循環創建線程,通過dx_invoke
執行回調,dx_push
和dx_invoke
是一一對應的
異步函數
同步函數
進入dispatch_sync
源碼,發現底層是通過柵欄函數
實現的
DISPATCH_NOINLINE
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);
}
-
進入
_dispatch_sync_f
源碼
_dispatch_sync_f -
進入
_dispatch_sync_f_inline
源碼,dq->dq_width == 1
表示串行隊列
- 柵欄函數
_dispatch_barrier_sync_f
, - 死鎖
_dispatch_sync_f_slow
,如果存在相互等待的情況下會死鎖
- 柵欄函數
DISPATCH_ALWAYS_INLINE
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)));//block執行并釋放
}
_dispatch_sync_f_slow死鎖
-
進入
_dispatch_sync_f_slow
,當前主隊列
是掛起、阻塞
_dispatch_sync_f_slow -
向隊列中添加任務,push加入主隊列,
_dispatch_trace_item_push
_dispatch_trace_item_push -
進入
__DISPATCH_WAIT_FOR_QUEUE__
,判斷dqu.dq是否為正在等待的主隊列,然后將dq的狀態和當前任務依賴的隊列進行匹配
__DISPATCH_WAIT_FOR_QUEUE__ 進入
_dq_state_drain_locked_by -> _dispatch_lock_is_locked_by
源碼,如果當前等待的隊列和正在執行任務的是同一個隊列,即線程ID是否相同,如果相同會造成死鎖
DISPATCH_ALWAYS_INLINE
static inline bool
_dispatch_lock_is_locked_by(dispatch_lock lock_value, dispatch_tid tid)
{
// equivalent to _dispatch_lock_owner(lock_value) == tid
//異或操作:相同為0,不同為1,如果相同,則為0,0 &任何數都為0
//即判斷 當前要等待的任務 和 正在執行的任務是否一樣,通俗的解釋就是 執行和等待的是否在同一隊列
return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}
總結
- 同步函數底層實現是
同步柵欄函數
- 如果
正在執行任務的隊列和等待的是同一個隊列
,會造成相互等待
的局面,形成死鎖
單例
進入dispatch_once
源碼實現,發現底層是通過dispatch_once_f
實現的
// val -- 靜態變量,由于不同位置定義的靜態變量是不同的,所以靜態變量具有唯一性
//block -- block回調
void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
- 進入
dispatch_once_f
源碼,其中的val
是外界傳入的onceToken
靜態變量,func
是_dispatch_Block_invoke(block)
DISPATCH_NOINLINE
void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
//靜態變量
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
//os_atomic_load獲取當前任務的標識符
#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))) {
//任務執行后,加鎖失敗,重新存儲,將標識符== DLOCK_ONCE_DONE
return _dispatch_once_mark_done_if_quiesced(l, v);
}
#endif
#endif
if (_dispatch_once_gate_tryenter(l)) {
//嘗試進入任務,解鎖,執行block回調
return _dispatch_once_callout(l, ctxt, func);
}
//如果此時有任務1正在執行,再次進來一個任務2,則`任務2`進入無限次等待
return _dispatch_once_wait(l);
}
_dispatch_once_gate_tryenter解鎖
底層通過os_atomic_cmpxchg
方法進行標識符
對比,如果對比沒問題進行解鎖
,即任務的標識符置為DLOCK_ONCE_UNLOCKED
DISPATCH_ALWAYS_INLINE
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);//首先對比,然后進行改變
}
_dispatch_once_callout 回調
進入_dispatch_once_callout
源碼
- 【第一步】
_dispatch_client_callout
:block回調 - 【第二步】
_dispatch_once_gate_broadcast
:廣播
DISPATCH_NOINLINE
static void
_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
dispatch_function_t func)
{
_dispatch_client_callout(ctxt, func);//block調用執行
_dispatch_once_gate_broadcast(l);//進行廣播:告訴別人有了歸屬,不要找我了
}
- 進入
_dispatch_client_callout
源碼,其中f
==_dispatch_Block_invoke(block)
,即異步回調
#undef _dispatch_client_callout
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
- 進入
_dispatch_once_gate_broadcast -> _dispatch_once_mark_done
,將任務的標識為DLOCK_ONCE_DONE
,即加鎖
DISPATCH_ALWAYS_INLINE
static inline uintptr_t
_dispatch_once_mark_done(dispatch_once_gate_t dgo)
{
//如果不相同,直接改為相同,然后上鎖 -- DLOCK_ONCE_DONE
return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
}
總結
【只執行一次的原理】:GCD單例中有兩個參數
onceToken 、block
,其中onceToken
是靜態變量,具有唯一性,在底層封裝成dispatch_once_gate_t
類型的變量l
,變量l
主要是用來獲取底層原子屬性關聯
,即變量v
,通過變量v
來查詢任務狀態,如果變量v == DLOCK_ONCE_DONE
,說明任務已經處理了,直接return【block調用時機】:如果任務沒有被執行,底層會通過C++函數的比較,任務狀態置為
DLOCK_ONCE_UNLOCK
,確保當前任務執行的唯一性
,之后進行block回調
,將任務置狀態為DLOCK_ONCE_DONE
,確保下次進來不會執行【多線程影響】:如果在當前任務執行期間,有其他任務進來,會進入
無限次等待
,原因是當前任務已經獲取了鎖,進行了加鎖
,其他任務是無法獲取鎖的
柵欄函數
-
控制任務執行順序
,可以讓其同步執行 只能控制同一并發隊列,只會阻塞一次
-
同步柵欄
添加進隊列時,當前線程會被加鎖
,直到同步柵欄之前的任務和同步柵欄本身的任務都執行完畢,當前線程才會解鎖 -
只有使用自定義隊列才有意義
,如果使用串行隊列
或者全局并發隊列
,這個柵欄函數的作用==同步函數
,沒有任何意義,還會耗費更多性能 -
dispatch_barrier_sync 同步柵欄函數
:阻塞線程
,影響主線程任務執行 -
dispatch_barrier_async 異步柵欄函數
:阻塞的是隊列且必須是自定義的并發隊列
,不影響主線程任務的執行
異步柵欄函數 底層分析
進入dispatch_barrier_async
源碼,發現底層和dispatch_async
類似
#ifdef __BLOCKS__
void
dispatch_barrier_async(dispatch_queue_t dq, dispatch_block_t work)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_BARRIER;
dispatch_qos_t qos;
qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
_dispatch_continuation_async(dq, dc, qos, dc_flags);
}
#endif
同步柵欄函數 底層分析
進入dispatch_barrier_sync
源碼
void
dispatch_barrier_sync(dispatch_queue_t dq, dispatch_block_t work)
{
uintptr_t dc_flags = DC_FLAG_BARRIER | DC_FLAG_BLOCK;
if (unlikely(_dispatch_block_has_private_data(work))) {
return _dispatch_sync_block_with_privdata(dq, work, dc_flags);
}
_dispatch_barrier_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}
_dispatch_barrier_sync_f_inline
進入_dispatch_barrier_sync_f --> _dispatch_barrier_sync_f_inline
源碼
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_barrier_sync_f_inline(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func, uintptr_t dc_flags)
{
dispatch_tid tid = _dispatch_tid_self();//獲取線程的id,即線程的唯一標識
...
//判斷線程狀態,需不需要等待,是否回收
if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(dl, tid))) {//柵欄函數也會死鎖
return _dispatch_sync_f_slow(dl, ctxt, func, DC_FLAG_BARRIER, dl,//沒有回收
DC_FLAG_BARRIER | dc_flags);
}
//驗證target是否存在,如果存在,加入柵欄函數的遞歸查找 是否等待
if (unlikely(dl->do_targetq->do_targetq)) {
return _dispatch_sync_recurse(dl, ctxt, func,
DC_FLAG_BARRIER | dc_flags);
}
_dispatch_introspection_sync_begin(dl);
_dispatch_lane_barrier_sync_invoke_and_complete(dl, ctxt, func
DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(
dq, ctxt, func, dc_flags | DC_FLAG_BARRIER)));//執行
}
主要分為以下幾步
通過
_dispatch_tid_self 獲取線程ID
-
通過
_dispatch_queue_try_acquire_barrier_sync
判斷線程狀態
_dispatch_queue_try_acquire_barrier_sync- 進入
_dispatch_queue_try_acquire_barrier_sync_and_suspend
源碼,在這里釋放
_dispatch_queue_try_acquire_barrier_sync_and_suspend
- 進入
通過
_dispatch_sync_recurse
遞歸查找柵欄函數的target通過
_dispatch_introspection_sync_begin
向前信息進行處理-
通過
_dispatch_lane_barrier_sync_invoke_and_complete
執行block并釋放
_dispatch_lane_barrier_sync_invoke_and_complete
信號量
-
使任務同步執行
,類似互斥鎖
- 控制GCD最大并發數
dispatch_semaphore_create 創建
初始化信號量,設置GCD最大并發數且最大并發數必須大于0
dispatch_semaphore_t
dispatch_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 waiting threads. Therefore it is bogus to
// initialize the semaphore with a negative value.
if (value < 0) {
return DISPATCH_BAD_INPUT;
}
dsema = _dispatch_object_alloc(DISPATCH_VTABLE(semaphore),
sizeof(struct dispatch_semaphore_s));
dsema->do_next = DISPATCH_OBJECT_LISTLESS;
dsema->do_targetq = _dispatch_get_default_queue(false);
dsema->dsema_value = value;
_dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
dsema->dsema_orig = value;
return dsema;
}
dispatch_semaphore_wait 加鎖
主要作用是對信號量dsema
通過os_atomic_dec2o
進行--
操作,內部執行C++函數atomic_fetch_sub_explicit
- value >= 0,
執行成功
- value == LONG_MIN,系統
拋出crash
- value < 0,進入
長等待
long
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
// dsema_value 進行 -- 操作
long value = os_atomic_dec2o(dsema, dsema_value, acquire);
if (likely(value >= 0)) {//表示執行操作無效,即執行成功
return 0;
}
return _dispatch_semaphore_wait_slow(dsema, timeout);//長等待
}
- 進入
_dispatch_semaphore_wait_slow
源碼,當value<0時,更加等待事件timeout做出不同操作
_dispatch_semaphore_wait_slow
dispatch_semaphore_signal解鎖
通過os_atomic_inc2o
函數對value
進行了++
操作,os_atomic_inc2o內部是通過C++的atomic_fetch_add_explicit
- value > 0,
執行成功
- value == 0,進入
長等待
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
//signal 對 value是 ++
long value = os_atomic_inc2o(dsema, dsema_value, release);
if (likely(value > 0)) {//返回0,表示當前的執行操作無效,相當于執行成功
return 0;
}
if (unlikely(value == LONG_MIN)) {
DISPATCH_CLIENT_CRASH(value,
"Unbalanced call to dispatch_semaphore_signal()");
}
return _dispatch_semaphore_signal_slow(dsema);//進入長等待
}
總結
-
dispatch_semaphore_create
初始化信號量,設置最大并發數 -
dispatch_semaphore_wait
對信號量value--,加鎖 -
dispatch_semaphore_signal
對信號量value++,解鎖
信號量流程圖
調度組
- 控制任務執行順序
dispatch_group_create 創建組
dispatch_group_async 進組任務
dispatch_group_notify 進組任務執行完畢通知 dispatch_group_wait 進組任務執行等待時間
//進組和出組一般是成對使用的
dispatch_group_enter 進組
dispatch_group_leave 出組
dispatch_group_create 創建組
- 進入
dispatch_group_create
源碼
dispatch_group_t
dispatch_group_create(void)
{
return _dispatch_group_create_with_count(0);
}
- 進入
_dispatch_group_create_with_count
源碼,對group對象屬性賦值,并返回group
DISPATCH_ALWAYS_INLINE
static inline dispatch_group_t
_dispatch_group_create_with_count(uint32_t n)
{
//創建group對象,類型為OS_dispatch_group
dispatch_group_t dg = _dispatch_object_alloc(DISPATCH_VTABLE(group),
sizeof(struct dispatch_group_s));
//group對象賦值
dg->do_next = DISPATCH_OBJECT_LISTLESS;
dg->do_targetq = _dispatch_get_default_queue(false);
if (n) {
os_atomic_store2o(dg, dg_bits,
(uint32_t)-n * DISPATCH_GROUP_VALUE_INTERVAL, relaxed);
os_atomic_store2o(dg, do_ref_cnt, 1, relaxed); // <rdar://22318411>
}
return dg;
}
dispatch_group_enter 進組
進入dispatch_group_enter
源碼,通過os_atomic_sub_orig2o
對dg->dg.bits
作 --
操作,對數值進行處理
void
dispatch_group_enter(dispatch_group_t dg)
{
// The value 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,//原子遞減 0 -> -1
DISPATCH_GROUP_VALUE_INTERVAL, acquire);
uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
if (unlikely(old_value == 0)) {//如果old_value
_dispatch_retain(dg); // <rdar://problem/22318411>
}
if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {//到達臨界值,會報crash
DISPATCH_CLIENT_CRASH(old_bits,
"Too many nested calls to dispatch_group_enter()");
}
}
dispatch_group_leave 出組
進入dispatch_group_leave
源碼,通過os_atomic_add_orig2o
對dg-> dg_state 作 `++操作
- -1到0,
++
- 根據狀態,do_while循環,喚醒block執行任務
- 如果0 + 1 = 1,enter-leave不平衡,即leave多次調用,會crash
void
dispatch_group_leave(dispatch_group_t dg)
{
// The value is incremented on a 64bits wide atomic so that the carry for
// the -1 -> 0 transition increments the generation atomically.
uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state,//原子遞增 ++
DISPATCH_GROUP_VALUE_INTERVAL, release);
uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK);
//根據狀態,喚醒
if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) {
old_state += DISPATCH_GROUP_VALUE_INTERVAL;
do {
new_state = old_state;
if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) {
new_state &= ~DISPATCH_GROUP_HAS_WAITERS;
new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
} else {
// If the group was entered again since the atomic_add above,
// we can't clear the waiters bit anymore as we don't know for
// which generation the waiters are for
new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
}
if (old_state == new_state) break;
} while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,
old_state, new_state, &old_state, relaxed)));
return _dispatch_group_wake(dg, old_state, true);//喚醒
}
//-1 -> 0, 0+1 -> 1,即多次leave,會報crash,簡單來說就是enter-leave不平衡
if (unlikely(old_value == 0)) {
DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
"Unbalanced call to dispatch_group_leave()");
}
}
- 進入
_dispatch_group_wake
源碼,do_while循環進行一步命中,調用_dispatch_continuation_async
DISPATCH_NOINLINE
static void
_dispatch_group_wake(dispatch_group_t dg, uint64_t dg_state, bool needs_release)
{
uint16_t refs = needs_release ? 1 : 0; // <rdar://problem/22318411>
if (dg_state & DISPATCH_GROUP_HAS_NOTIFS) {
dispatch_continuation_t dc, next_dc, tail;
// Snapshot before anything is notified/woken <rdar://problem/8554546>
dc = os_mpsc_capture_snapshot(os_mpsc(dg, dg_notify), &tail);
do {
dispatch_queue_t dsn_queue = (dispatch_queue_t)dc->dc_data;
next_dc = os_mpsc_pop_snapshot_head(dc, tail, do_next);
_dispatch_continuation_async(dsn_queue, dc,
_dispatch_qos_from_pp(dc->dc_priority), dc->dc_flags);//block任務執行
_dispatch_release(dsn_queue);
} while ((dc = next_dc));//do-while循環,進行異步任務的命中
refs++;
}
if (dg_state & DISPATCH_GROUP_HAS_WAITERS) {
_dispatch_wake_by_address(&dg->dg_gen);//地址釋放
}
if (refs) _dispatch_release_n(dg, refs);//引用釋放
}
- 進入
_dispatch_continuation_async
源碼,與異步函數的block回調一致
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#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);//與dx_invoke一樣,都是宏
}
dispatch_group_notify 通知
進入dispatch_group_notify
源碼,如果old_state==0
,就可以進行釋放了,
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);
//獲取dg底層的狀態標識碼,通過os_atomic_store2o獲取的值,即從dg的狀態碼 轉成了 os底層的state
prev = os_mpsc_push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
if (os_mpsc_push_was_empty(prev)) _dispatch_retain(dg);
os_mpsc_push_update_prev(os_mpsc(dg, dg_notify), prev, dsn, do_next);
if (os_mpsc_push_was_empty(prev)) {
os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, release, {
new_state = old_state | DISPATCH_GROUP_HAS_NOTIFS;
if ((uint32_t)old_state == 0) { //如果等于0,則可以進行釋放了
os_atomic_rmw_loop_give_up({
return _dispatch_group_wake(dg, new_state, false);//喚醒
});
}
});
}
}
dispatch_group_async
進入dispatch_group_async
源碼,包裝任務和異步處理任務
,底層實際就是enter-leave
#ifdef __BLOCKS__
void
dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_block_t db)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_GROUP_ASYNC;
dispatch_qos_t qos;
//任務包裝器
qos = _dispatch_continuation_init(dc, dq, db, 0, dc_flags);
//處理任務
_dispatch_continuation_group_async(dg, dq, dc, qos);
}
#endif
- 進入
_dispatch_continuation_group_async
源碼,封裝了dispatch_group_enter
進組操作
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_continuation_t dc, dispatch_qos_t qos)
{
dispatch_group_enter(dg);//進組
dc->dc_data = dg;
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);//異步操作
}
- 搜索
_dispatch_client_callout
的調用,在_dispatch_continuation_with_group_invoke
中
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_with_group_invoke(dispatch_continuation_t dc)
{
struct dispatch_object_s *dou = dc->dc_data;
unsigned long type = dx_type(dou);
if (type == DISPATCH_GROUP_TYPE) {//如果是調度組類型
_dispatch_client_callout(dc->dc_ctxt, dc->dc_func);//block回調
_dispatch_trace_item_complete(dc);
dispatch_group_leave((dispatch_group_t)dou);//出組
} else {
DISPATCH_INTERNAL_CRASH(dx_type(dou), "Unexpected object type");
}
總結
-
enter-leave
必須成對出現 -
dispatch_group_enter
底層通過C++函數,對group的value進行--操作(0->-1) -
dispatch_group_leave
底層通過C++函數,對group的value進行++操作(-1 ->0) -
dispatch_group_notify
底層通過判斷group的state
是否等于0
,當==0時來進行通知 - block中任務的喚醒可以通過
dispatch_group_leave
和dispatch_group_notify
-
dispatch_group_async
底層得實現就是enter-leave
調度組原理圖
dispatch_source
基礎數據類型
,用于協調特定底層系統事件的處理
代替異步回調函數,來處理系統相關的事件,當配置一個dispatch時,需要制定檢測的
事件
,dispatchqueue
,處理事件的代碼(block或函數)
,當事件發生時,dispatch source
會提交你的block或函數到queue
中執行-
相對于
dispatch_async
的優勢是聯結和CPU負荷小
,簡單來說就是由你調用dispatch_source_merge_data
函數來向自己發送信號- 聯結:在任一線程上調度它的一個函數
dispatch_source_merge_data
,就會執行dispatch source
事先定義好的句柄
(block),這個過程叫做Custom event ,用戶事件
- 句柄:
一種指向指針的指針
,指向的就是一個類或者結構,和系統有密切關系- 實例句柄 HINSTANCE
- 位圖句柄 HBITMAP
- 設備表句柄 HDC
- 圖標句柄 HICON
- 聯結:在任一線程上調度它的一個函數
使用
-
type
:dispatch處理的事件 -
handle
:可以理解為句柄、索引或id,假如要監聽進程,需要傳入進程的ID -
mask
:可以理解為描述,提供更詳細的描述,讓它知道具體要監聽什么 -
queue
:自定義源需要的一個隊列,用來處理所有的響應句柄
dispatch_source_t source = dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)
Dispatch Source 種類
-
DISPATCH_SOURCE_TYPE_DATA_ADD
:自定義的事件,變量增加,當同一時間,一個事件的的觸發頻率很高,那么Dispatch Source會將這些響應以ADD的方式進行累積,然后等系統空閑時最終處理,如果觸發頻率比較零散,那么Dispatch Source會將這些事件分別響應。 -
DISPATCH_SOURCE_TYPE_DATA_OR
:自定義的事件,和DISPATCH_SOURCE_TYPE_DATA_ADD一樣,但是是以OR的方式進行累積 -
DISPATCH_SOURCE_TYPE_MACH_SEND
:MACH端口發送 -
DISPATCH_SOURCE_TYPE_MACH_RECV
:MACH端口接收 -
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE
:內存壓力 (注:iOS8后可用) -
DISPATCH_SOURCE_TYPE_PROC
:進程監聽,如進程的退出、創建一個或更多的子線程、進程收到UNIX信號 -
DISPATCH_SOURCE_TYPE_READ
:IO操作,如對文件的操作、socket操作的讀響應 -
DISPATCH_SOURCE_TYPE_SIGNAL
:接收到UNIX信號時響應 -
DISPATCH_SOURCE_TYPE_TIMER
:定時器 -
DISPATCH_SOURCE_TYPE_VNODE
:文件狀態監聽,文件被刪除、移動、重命名 -
DISPATCH_SOURCE_TYPE_WRITE
:IO操作,如對文件的操作、socket操作的寫響應
常見函數
//掛起隊列
dispatch_suspend(queue)
//分派源創建時默認處于暫停狀態,在分派源分派處理程序之前必須先恢復
dispatch_resume(source)
//向分派源發送事件,需要注意的是,不可以傳遞0值(事件不會被觸發),同樣也不可以傳遞負數。
dispatch_source_merge_data
//設置響應分派源事件的block,在分派源指定的隊列上運行
dispatch_source_set_event_handler
//得到分派源的數據
dispatch_source_get_data
//得到dispatch源創建,即調用dispatch_source_create的第二個參數
uintptr_t dispatch_source_get_handle(dispatch_source_t source);
//得到dispatch源創建,即調用dispatch_source_create的第三個參數
unsigned long dispatch_source_get_mask(dispatch_source_t source);
////取消dispatch源的事件處理--即不再調用block。如果調用dispatch_suspend只是暫停dispatch源。
void dispatch_source_cancel(dispatch_source_t source);
//檢測是否dispatch源被取消,如果返回非0值則表明dispatch源已經被取消
long dispatch_source_testcancel(dispatch_source_t source);
//dispatch源取消時調用的block,一般用于關閉文件或socket等,釋放相關資源
void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler);
//可用于設置dispatch源啟動時調用block,調用完成后即釋放這個block。也可在dispatch源運行當中隨時調用這個函數。
void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_t registration_handler);
使用場景
用于驗證碼倒計時,因為dispatch_source不依賴于Runloop,而是直接和底層內核交互,準確性更高。
- (void)use033{
//倒計時時間
__block int timeout = 3;
//創建隊列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
//創建timer
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, globalQueue);
//設置1s觸發一次,0s的誤差
/*
- source 分派源
- start 數控制計時器第一次觸發的時刻。參數類型是 dispatch_time_t,這是一個opaque類型,我們不能直接操作它。我們得需要 dispatch_time 和 dispatch_walltime 函數來創建它們。另外,常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用。
- interval 間隔時間
- leeway 計時器觸發的精準程度
*/
dispatch_source_set_timer(timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0);
//觸發的事件
dispatch_source_set_event_handler(timer, ^{
//倒計時結束,關閉
if (timeout <= 0) {
//取消dispatch源
dispatch_source_cancel(timer);
}else{
timeout--;
dispatch_async(dispatch_get_main_queue(), ^{
//更新主界面的操作
NSLog(@"倒計時 - %d", timeout);
});
}
});
//開始執行dispatch源
dispatch_resume(timer);
}