欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS開發(fā)探索多線程GCD常用函數(shù)

 更新時(shí)間:2022年07月26日 10:44:33   作者:懶的問蒼天  
這篇文章主要為大家介紹了iOS開發(fā)探索多線程GCD常用函數(shù)的使用示例介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

前篇文章我們了解了GCD的任務(wù)的原理,接下來我們在探索一下GCD中我們開發(fā)常用的函數(shù)

單例

下面我們從源碼中看一下我們創(chuàng)建單例的時(shí)候使用的dispatch_once,都做了什么,是通過什么操作保證全局唯一的

void dispatch_once(dispatch_once_t *val, dispatch_block_t block) {
    dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
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);
}
// 原子屬性,比較 + 交換 &l->dgo_once 是否等于 DLOCK_ONCE_UNLOCKED,相等還沒有執(zhí)行過,將_dispatch_lock_value_for_self() 賦值給 &l->dgo_once,返回true;不等返回false
#define os_atomic_cmpxchg(p, e, v, m) \
    ({ _os_atomic_basetypeof(p) _r = (e); \
    atomic_compare_exchange_strong_explicit(_os_atomic_c11_atomic(p), \
    &_r, v, memory_order_##m, memory_order_relaxed); })
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);
}

和任務(wù)一樣,對block進(jìn)行了封裝,同時(shí)通過判斷dispatch_once_t *val的狀態(tài)進(jìn)行判斷,DLOCK_ONCE_DONE直接返回,也就是已經(jīng)執(zhí)行了直接返回,調(diào)用_dispatch_once_gate_tryenter判斷是否執(zhí)行過block,未執(zhí)行_dispatch_once_callout進(jìn)行執(zhí)行代碼,執(zhí)行過調(diào)用_dispatch_once_wait等待執(zhí)行結(jié)束。

static void _dispatch_once_callout(dispatch_once_gate_t l, void *ctxt, dispatch_function_t func) {
    _dispatch_client_callout(ctxt, func); // 執(zhí)行函數(shù)
    _dispatch_once_gate_broadcast(l); // 標(biāo)記執(zhí)行完成
}
static inline void _dispatch_once_gate_broadcast(dispatch_once_gate_t l) {
    dispatch_lock value_self = _dispatch_lock_value_for_self();
    uintptr_t v;
    #if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
        v = _dispatch_once_mark_quiescing(l);
    #else
        v = _dispatch_once_mark_done(l); // 將 &l->dgo_once 設(shè)置成 DLOCK_ONCE_DONE,標(biāo)記函數(shù)已經(jīng)執(zhí)行完成了,廣播的作用
    #endif
    if (likely((dispatch_lock)v == value_self)) return;
    _dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);
}
void _dispatch_once_wait(dispatch_once_gate_t dgo) {
    ...省略部分...
    for (;;) {
        // 從底層獲取 &dgo->dgo_onc 的狀態(tài)
        os_atomic_rmw_loop(&dgo->dgo_once, old_v, new_v, relaxed, {
            if (likely(old_v == DLOCK_ONCE_DONE)) {
                os_atomic_rmw_loop_give_up(return); // 退出循環(huán)
            }
            #if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
            if (DISPATCH_ONCE_IS_GEN(old_v)) {
                os_atomic_rmw_loop_give_up({
                    os_atomic_thread_fence(acquire);
                    return _dispatch_once_mark_done_if_quiesced(dgo, old_v);
                });
            }
            #endif
            new_v = old_v | (uintptr_t)DLOCK_WAITERS_BIT;
            if (new_v == old_v) os_atomic_rmw_loop_give_up(break);
        });
        ...省略部分...
    }
}

_dispatch_once_wait中使用死循環(huán),直到DLOCK_ONCE_DONE時(shí)調(diào)用os_atomic_rmw_loop_give_up(return);退出循環(huán),通過這種方式保證只創(chuàng)建一份.

柵欄函數(shù)

柵欄函數(shù):前面的任務(wù)沒有執(zhí)行完成時(shí),不執(zhí)行柵欄函數(shù)中的任務(wù),柵欄函數(shù)后面的任務(wù)需等待柵欄函數(shù)中的任務(wù)執(zhí)行完成才能執(zhí)行。

  • 柵欄函數(shù)只能攔截同一隊(duì)列中的任務(wù)
  • 柵欄函數(shù)無法攔截全局隊(duì)列,因?yàn)橄到y(tǒng)操作也會(huì)使用全局隊(duì)列
  • 攔截同步隊(duì)列和普通的任務(wù)原理相同 柵欄函數(shù)分為同步函數(shù)和異步函數(shù),我們先看一下同步函數(shù)的源碼dispatch_barrier_sync
void dispatch_barrier_sync(dispatch_queue_t dq, dispatch_block_t work) {
    ...省略...
    _dispatch_barrier_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags); // 和之前一樣的封裝block
}
_dispatch_barrier_sync_f -> _dispatch_barrier_sync_f_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();
    dispatch_lane_t dl = upcast(dq)._dl;
    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);
    }
    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_barrier_sync_f_inline函數(shù)中有3個(gè)執(zhí)行func的函數(shù), 我們將這三個(gè)函數(shù)使用符號斷點(diǎn)來查看其調(diào)用的是哪個(gè)函數(shù),發(fā)現(xiàn)調(diào)用的是_dispatch_sync_f_slow

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) {
    dispatch_queue_t top_dq = top_dqu._dq;
    dispatch_queue_t dq = dqu._dq;
    if (unlikely(!dq->do_targetq)) {
        return _dispatch_sync_function_invoke(dq, ctxt, func);
    }
    ...省略部分...
    // 等待前面的任務(wù)執(zhí)行完成
    _dispatch_trace_item_push(top_dq, &dsc);
    __DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);
    if (dsc.dsc_func == NULL) {
        // dsc_func being cleared means that the block ran on another thread ie.
        // case (2) as listed in _dispatch_async_and_wait_f_slow.
        dispatch_queue_t stop_dq = dsc.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
}

繼續(xù)查看_dispatch_sync_f_slow的源碼, 內(nèi)部執(zhí)行func的函數(shù)有兩個(gè),也加入符號斷點(diǎn)查看,最后得到調(diào)用的是_dispatch_sync_invoke_and_complete_recurse, 該方法就是同步任務(wù)調(diào)用的執(zhí)行block任務(wù)的函數(shù), 該函數(shù)內(nèi)部有個(gè)_dispatch_sync_complete_recurse函數(shù)

static void _dispatch_sync_complete_recurse(dispatch_queue_t dq, dispatch_queue_t stop_dq, uintptr_t dc_flags){
bool barrier = (dc_flags & DC_FLAG_BARRIER);
    do {
        if (dq == stop_dq) return;
        if (barrier) {
            dx_wakeup(dq, 0, DISPATCH_WAKEUP_BARRIER_COMPLETE);
        } else {
            _dispatch_lane_non_barrier_complete(upcast(dq)._dl, 0);
        }
        dq = dq->do_targetq;
        barrier = (dq->dq_width == 1);
    } while (unlikely(dq->do_targetq));
}

該函數(shù)是使用do while,內(nèi)部使用barrier來判斷是否使用柵欄函數(shù),沒有柵欄函數(shù)就調(diào)用隊(duì)列中接下來的任務(wù),有柵欄函數(shù)就調(diào)用dx_wakeup

#define dx_wakeup(x, y, z) dx_vtable(x)->dq_wakeup(x, y, z)

dq_wakeup和之前異步任務(wù)的dq_push一樣,是針對不同隊(duì)列調(diào)用不同的方法

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
    .do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
    ......
    .dq_wakeup        = _dispatch_lane_wakeup,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
    .do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
    ......
    .dq_wakeup        = _dispatch_lane_wakeup,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
    .do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
    ......
    .dq_wakeup        = _dispatch_root_queue_wakeup,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type        = DISPATCH_QUEUE_MAIN_TYPE,
    ......
    .dq_wakeup        = _dispatch_main_queue_wakeup,
);
// 串行隊(duì)列及自己創(chuàng)建的并發(fā)隊(duì)列
void _dispatch_lane_wakeup(dispatch_lane_class_t dqu, dispatch_qos_t qos, dispatch_wakeup_flags_t flags){
dispatch_queue_wakeup_target_t target = DISPATCH_QUEUE_WAKEUP_NONE;
    if (unlikely(flags & DISPATCH_WAKEUP_BARRIER_COMPLETE)) {
        // 柵欄函數(shù)任務(wù)完成后調(diào)用該函數(shù)
        return _dispatch_lane_barrier_complete(dqu, qos, flags);
    }
    if (_dispatch_queue_class_probe(dqu)) {
        target = DISPATCH_QUEUE_WAKEUP_TARGET;
    }
    // 喚醒后面隊(duì)列中的任務(wù),執(zhí)行柵欄函數(shù)后面隊(duì)列里的任務(wù)
    return _dispatch_queue_wakeup(dqu, qos, flags, target);
}
// 全局并發(fā)隊(duì)列 沒有任何關(guān)于柵欄函數(shù)的操作,所以柵欄函數(shù)對全局并發(fā)隊(duì)列無效
void _dispatch_root_queue_wakeup(dispatch_queue_global_t dq, DISPATCH_UNUSED dispatch_qos_t qos, dispatch_wakeup_flags_t flags){
    if (!(flags & DISPATCH_WAKEUP_BLOCK_WAIT)) {
        DISPATCH_INTERNAL_CRASH(dq->dq_priority, "Don't try to wake up or override a root queue");
    }
    if (flags & DISPATCH_WAKEUP_CONSUME_2) {
        return _dispatch_release_2_tailcall(dq);
    }
}

我們可以看到全局并發(fā)隊(duì)列的dq_wakeup關(guān)聯(lián)的函數(shù)中沒有任何關(guān)于柵欄函數(shù)的操作,所以柵欄函數(shù)對全局并發(fā)隊(duì)列無效.

可是柵欄函數(shù)到底是怎么攔截的呢?

我們還是用全局符號斷點(diǎn)來進(jìn)行查看,將柵欄函數(shù)前面的任務(wù)進(jìn)行延時(shí)操作,我們運(yùn)行發(fā)現(xiàn),調(diào)用_dispatch_sync_f_slow函數(shù)后,并沒有立即調(diào)用_dispatch_sync_invoke_and_complete_recurse,而是等到我們前面的延時(shí)操作結(jié)束后,才進(jìn)行的_dispatch_sync_invoke_and_complete_recurse調(diào)用, 也就是說在_dispatch_sync_f_slow函數(shù)調(diào)用后,會(huì)等待前面的函數(shù)全部執(zhí)行完成后,才會(huì)執(zhí)行柵欄函數(shù)本身的任務(wù)及喚醒柵欄函數(shù)后面的任務(wù).

那為什么柵欄函數(shù)還區(qū)分同步和異步函數(shù)呢?

其實(shí)就是柵欄函數(shù)本身的任務(wù)是否需要開辟線程去進(jìn)行執(zhí)行來區(qū)分使用同步還是異步函數(shù)

調(diào)度組 dispatch_group_t

柵欄函數(shù)有自身的局限性,他只能攔截同一隊(duì)列中的任務(wù),當(dāng)有多個(gè)隊(duì)列的任務(wù)時(shí),無法生效,這是我們應(yīng)該使用調(diào)度組dispatch_group_t來進(jìn)行任務(wù)攔截,保障任務(wù)的執(zhí)行順序. dispatch_group_t有兩種書寫方式:

dispatch_group_async(g, dispatch_get_global_queue(0, 0), ^{});

dispatch_group_enter(g); + dispatch_group_leave(g);

這兩種書寫方式,效果是一模一樣的,dispatch_group_async就是dispatch_group_enter和dispatch_group_leave的整合

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);
}
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);
}
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);
}

dispatch_group_async其內(nèi)部先進(jìn)行dispatch_group_enter,然后和異步函數(shù)一樣進(jìn)行了dx_push調(diào)用來執(zhí)行任務(wù),和我們之前的異步任務(wù)是一樣的流程,在_dispatch_root_queues_init_once-...->_dispatch_continuation_invoke_inline文件里我們可以看到dispatch_group_leave的調(diào)用

static inline void _dispatch_continuation_invoke_inline(dispatch_object_t dou, dispatch_invoke_flags_t flags, dispatch_queue_class_t dqu) {
    ...省略部分...
    if (unlikely(dc_flags & DC_FLAG_GROUP_ASYNC)) {
        _dispatch_continuation_with_group_invoke(dc);
    }
    ...省略部分...
}
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);
        _dispatch_trace_item_complete(dc);
        dispatch_group_leave((dispatch_group_t)dou);
    } else {
        DISPATCH_INTERNAL_CRASH(dx_type(dou), "Unexpected object type");
    }
}

下面我們查看一下dispatch_group_enter、dispatch_group_leave的源碼

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, DISPATCH_GROUP_VALUE_INTERVAL, acquire);
    uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
    if (unlikely(old_value == 0)) {
        _dispatch_retain(dg); // <rdar://problem/22318411>
    }
    if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {
        DISPATCH_CLIENT_CRASH(old_bits,
        "Too many nested calls to dispatch_group_enter()");
    }
}
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.
    // 修改 dg_state
    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);
    }
    if (unlikely(old_value == 0)) {
        DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
        "Unbalanced call to dispatch_group_leave()");
    }
}

從源碼中我們可以看到dispatch_group_enter進(jìn)行減1操作,增加組內(nèi)當(dāng)前未完成任務(wù)的引用計(jì)數(shù); dispatch_group_leave進(jìn)行加1操作,減少組內(nèi)未完成任務(wù)的引用計(jì)數(shù);

當(dāng)引用計(jì)數(shù)變成0,就會(huì)調(diào)用dispatch_group_notify來執(zhí)行后續(xù)代碼.

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_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)) {
        // 監(jiān)聽 dg_state
        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) {
                os_atomic_rmw_loop_give_up({
                    return _dispatch_group_wake(dg, new_state, false);
                });
            }
        });
    }
}

我們可以看到dispatch_group_notify中會(huì)判斷old_state == 0才會(huì)執(zhí)行后續(xù)代碼,如果dispatch_group_enter和dispatch_group_leave不是一一對應(yīng)的則永遠(yuǎn)不會(huì)執(zhí)行dispatch_group_notify,而且dispatch_group_leave中對old_value == 0,也進(jìn)行了crash判斷,dispatch_group_leave比dispatch_group_enter多的話會(huì)直接crash.

信號量 dispatch_semaphore_t

GCD中另一種常用的控制任務(wù)執(zhí)行順序的就是信號量,其主要是控制并發(fā)數(shù)量

  • 通過dispatch_semaphore_create(0)來創(chuàng)建信號量并指定信號的大小
  • dispatch_semaphore_signal(sem), 發(fā)送信號量,將信號量的值 +1
  • dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER) 等待信號量, 將信號量的值 -1, 當(dāng)信號值的值小于0時(shí)會(huì)阻塞線程一直進(jìn)行等待,當(dāng)信號值的值大于等于0時(shí)執(zhí)行后續(xù)代碼

接下來我們還是查看源碼來看看信號量的實(shí)現(xiàn)原理

intptr_t dispatch_semaphore_signal(dispatch_semaphore_t dsema) {
    // 對信號量的值 + 1
    long value = os_atomic_inc2o(dsema, dsema_value, release);
    if (likely(value > 0)) {
        return 0;
    }
    if (unlikely(value == LONG_MIN)) {
        DISPATCH_CLIENT_CRASH(value, "Unbalanced call to dispatch_semaphore_signal()");
    }
    return _dispatch_semaphore_signal_slow(dsema);
}
intptr_t _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema) {
    _dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
    _dispatch_sema4_signal(&dsema->dsema_sema, 1);
    return 1;
}

我們可以看到signal就只有+1操作,下面我們主要看一下wait是如何進(jìn)行等待的

intptr_t dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) {
    // 對信號量進(jìn)行減1操作
    long value = os_atomic_dec2o(dsema, dsema_value, acquire);
    // 當(dāng)信號量的值 >= 0直接返回,執(zhí)行后續(xù)代碼
    if (likely(value >= 0)) {
        return 0;
    }
    return _dispatch_semaphore_wait_slow(dsema, timeout);
}
static intptr_t _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout){
    long orig;
    _dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
    switch (timeout) {
        default:
            if (!_dispatch_sema4_timedwait(&dsema->dsema_sema, timeout)) {
                break;
            }
        // Fall through and try to undo what the fast path did to
        // dsema->dsema_value
        case DISPATCH_TIME_NOW:
            orig = dsema->dsema_value;
            while (orig < 0) {
                if (os_atomic_cmpxchgv2o(dsema, dsema_value, orig, orig + 1, &orig, relaxed)) {
                    return _DSEMA4_TIMEOUT();
                }
            }
        // Another thread called semaphore_signal().
        // Fall through and drain the wakeup.
        case DISPATCH_TIME_FOREVER:
            _dispatch_sema4_wait(&dsema->dsema_sema);
            break;
    }
    return 0;
}
void _dispatch_sema4_wait(_dispatch_sema4_t *sema) {
    int ret = 0;
    do {
        ret = sem_wait(sema);
    } while (ret == -1 && errno == EINTR);
    DISPATCH_SEMAPHORE_VERIFY_RET(ret);
}

我們可以看到wait進(jìn)行等待的函數(shù) 是根據(jù)我們傳遞的timeout進(jìn)行的判斷,然后進(jìn)行do while循環(huán)阻塞當(dāng)前線程獲取信號量的值,如果信號量>=0跳出循環(huán),執(zhí)行后續(xù)代碼.

  • 需要注意的是dispatch_semaphore_signal的數(shù)量不能比dispatch_semaphore_wait少,否則信號量內(nèi)存無法被釋放,會(huì)導(dǎo)致程序崩潰,_dispatch_semaphore_dispose函數(shù)中會(huì)進(jìn)行判斷當(dāng)前的信號量的值 < 原始信號量的值 會(huì)觸發(fā)崩潰.
void _dispatch_semaphore_dispose(dispatch_object_t dou, DISPATCH_UNUSED bool *allow_free) {
    dispatch_semaphore_t dsema = dou._dsema;
    if (dsema->dsema_value < dsema->dsema_orig) {
        DISPATCH_CLIENT_CRASH(dsema->dsema_orig - dsema->dsema_value, "Semaphore object deallocated while in use");
    }
    _dispatch_sema4_dispose(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
}

dispatch_source

dispatch_source 和runloop的source相同, 用于監(jiān)聽事件的,比如計(jì)時(shí)器,系統(tǒng)內(nèi)存壓力,mach port 待處理消息等.

dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0)) 創(chuàng)建事件源

dispatch_source_set_event_handler(source, ^{}) 設(shè)置源事件回調(diào)

dispatch_source_merge_data(source, 1) 設(shè)置源事件數(shù)據(jù)

dispatch_source_get_data(source) 獲取源事件數(shù)據(jù)

dispatch_resume(source); 啟動(dòng)/繼續(xù)

dispatch_suspend(source); 掛起,暫停

dispatch_source_cancel(source); 異步取消源事件

dispatch_cancel(source); 取消源事件

總結(jié)

柵欄函數(shù)攔截同一隊(duì)列中的任務(wù),無法攔截全局隊(duì)列,因?yàn)橄到y(tǒng)操作也會(huì)使用全局隊(duì)列,攔截同步隊(duì)列就是普通的任務(wù)原理相同

調(diào)度組是通過未完成任務(wù)的引用計(jì)數(shù)來控制組里面任務(wù)的執(zhí)行順序

信號量其主要是控制并發(fā)數(shù)量,

以上就是iOS開發(fā)探索多線程GCD常用函數(shù)的詳細(xì)內(nèi)容,更多關(guān)于iOS開發(fā)多線程GCD函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • iOS實(shí)現(xiàn)類似格瓦拉電影的轉(zhuǎn)場動(dòng)畫

    iOS實(shí)現(xiàn)類似格瓦拉電影的轉(zhuǎn)場動(dòng)畫

    這篇文章主要給大家介紹了利用iOS如何實(shí)現(xiàn)類似格瓦拉電影的轉(zhuǎn)場動(dòng)畫,文中給出了詳細(xì)步驟實(shí)現(xiàn)代碼,對大家的學(xué)習(xí)和理解很有幫助,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-11-11
  • Objective-C中字符串的拼接方法小結(jié)

    Objective-C中字符串的拼接方法小結(jié)

    這篇文章主要介紹了Objective-C中字符串的拼接方法小結(jié),除了依靠NSString,文中還介紹了在宏里拼接字符串的方法,需要的朋友可以參考下
    2016-02-02
  • iOS實(shí)現(xiàn)比例拼圖的方法示例

    iOS實(shí)現(xiàn)比例拼圖的方法示例

    這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)比例拼圖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者開發(fā)iOS具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • OC - 9.基于Quartz2D繪制下載進(jìn)度條(demo)

    OC - 9.基于Quartz2D繪制下載進(jìn)度條(demo)

    這篇文章主要介紹了OC - 9.基于Quartz2D繪制下載進(jìn)度條(demo)的相關(guān)資料,需要的朋友可以參考下
    2015-11-11
  • iOS中設(shè)置圓角的幾種方法示例

    iOS中設(shè)置圓角的幾種方法示例

    這篇文章主要介紹了iOS中設(shè)置圓角的三種方法,其中包括使用layer屬性、使用繪圖設(shè)置圓角以及通過另一張mask圖創(chuàng)建新圖,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-03-03
  • 詳解iOS中多線程app開發(fā)的GCD隊(duì)列的使用

    詳解iOS中多線程app開發(fā)的GCD隊(duì)列的使用

    這篇文章主要介紹了詳解iOS中多線程app開發(fā)的GCD隊(duì)列的使用,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下
    2015-12-12
  • iOS 攔截重定向302跳轉(zhuǎn)的方法詳解

    iOS 攔截重定向302跳轉(zhuǎn)的方法詳解

    這篇文章主要介紹了iOS 攔截重定向302跳轉(zhuǎn)的方法詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • iOS中精確計(jì)算WebView高度的方法示例

    iOS中精確計(jì)算WebView高度的方法示例

    這篇文章主要給大家介紹了關(guān)于iOS中如何精確計(jì)算WebView高度,以及iOS開發(fā)之解決WebView自適應(yīng)內(nèi)容高度的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧
    2018-05-05
  • IOS實(shí)現(xiàn)百度地圖自定義大頭針和氣泡樣式

    IOS實(shí)現(xiàn)百度地圖自定義大頭針和氣泡樣式

    這篇文章主要介紹了 IOS百度地圖自定義大頭針和氣泡的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下
    2016-12-12
  • iOS中NSNumberFormatter的介紹與用法

    iOS中NSNumberFormatter的介紹與用法

    NSNumberFormatter 應(yīng)該可以滿足你對數(shù)據(jù)形式的一般需求,值得了解一下,下面這篇文章主要給大家介紹了關(guān)于iOS中NSNumberFormatter的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-09-09

最新評論