dispatch_barrier (栅栏)
在访问数据库或文件时,如前所述,使用 Serial Dispatch Queue 可避免数据竟争的问题。
写入处理确实不可与其他的写入处理以及包含读取处理的其他某些处理并行执行。但是如果读取处理只是与读取处理并行执行,那么多个并行执行就不会发生问题。也就是说,为了高效率地进行访问,读取处理追加到 Concurrent Dispatch Queue 中,写入处理在任一个读取处理没有执行的状态下,追加到 Serial Dispatch Queue 中即可(在写入处理结束之前,读取处理不可执行)。
虽然利用 Dispatch Group 和 dispatch_set_target_queue
函数也可实现,但是源代码会很复杂。 GCD 为我们提供了更为聪明的解决方法 dispateh_barrier_async
函数。该函数同 dispatch_queue_crcate
函数生成的 Concurrent Dispatch Queue 一起使用。
-
dispatch_barrier_async
控制任务执⾏顺序,前⾯的任务执⾏完毕才会来到这⾥ -
dispatch_barrier_sync
作⽤相同,但是这个会堵塞线程,影响后⾯的任务执⾏
栅栏函数只能控制同⼀并发队列
dispatch_barrier_async
函数会等待追加到 Concurrent Dispatch Queue 上的并行执行的处理全部结束之后,再将指定的处理追加到该 Concurrent Dispatch Queue 中。然后在由 dispatch_barrier_async
函数追加的处理执行完毕后, Concurrent Dispatch Queue 才恢复为一般的动作,追加到该 Concurrent Dispatch Queue 的处理又开始并行执行。如下仅使用 dispatch_barrier_async
函数代替 dispatch_async
函数即可。
dispatch_async(queue, blk0ForReading);
dispatch_async(queue, blk1ForReading);
dispatch_async(queue, blk2ForReading);
dispatch_async(queue, blk0ForReading);
dispatch_barrier_async(queue,blkForWriting);
dispatch_async(queue, blk4ForReading);
dispatch_async(queue, blk5ForReading);
dispatch_barrier_sync 实现
在 libdispatch 中搜索 dispatch_barrier_sync(dispatch_queue_t dq, dispatch_block_t work)
函数,可以看到函数调用栈为 dispatch_barrier_sync
-> _dispatch_barrier_sync_f
-> _dispatch_barrier_sync_f_inline
;
void
dispatch_barrier_sync(dispatch_queue_t dq, dispatch_block_t work)
{
//...
_dispatch_barrier_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}
DISPATCH_NOINLINE
static void
_dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func, uintptr_t dc_flags)
{
_dispatch_barrier_sync_f_inline(dq, ctxt, func, dc_flags);
}
_dispatch_barrier_sync_f_inline
继续查看 _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();
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;
//刚刚获得进入队列状态的屏障锁
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_barrier_sync_f_inline
执行流程如下:
通过
_dispatch_tid_self
获取线程ID通过
_dispatch_queue_try_acquire_barrier_sync
判断线程状态,尝试挂起线程通过
_dispatch_sync_recurse
递归查找栅栏函数的target通过
_dispatch_lane_barrier_sync_invoke_and_complete
执行block
并释放
_dispatch_lane_barrier_sync_invoke_and_complete
继续查看 _dispatch_lane_barrier_sync_invoke_and_complete
函数
DISPATCH_NOINLINE
static void
_dispatch_lane_barrier_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);
//...
// similar to _dispatch_queue_drain_try_unlock
os_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, release, {
new_state = old_state - DISPATCH_QUEUE_SERIAL_DRAIN_OWNED;
new_state &= ~DISPATCH_QUEUE_DRAIN_UNLOCK_MASK;
new_state &= ~DISPATCH_QUEUE_MAX_QOS_MASK;
if (unlikely(old_state & fail_unlock_mask)) {
os_atomic_rmw_loop_give_up({
return _dispatch_lane_barrier_complete(dq, 0, flags);
});
}
});
if (_dq_state_is_base_wlh(old_state)) {
_dispatch_event_loop_assert_not_owned((dispatch_wlh_t)dq);
}
}
_dispatch_lane_barrier_sync_invoke_and_complete 函数执行流程如下:
_dispatch_sync_function_invoke_inline
同步执行block
,与dispatch_sync
一致os_atomic_rmw_loop2o
进行状态释放,调用dispatch_lane_barrier_complete
执行完成。
dispatch_barrier_async 实现
查看 dispatch_barrier_async
可以发现与 dispatch_async
类似,差别在于 DC_FLAG_BARRIER
,根据 DC_FLAG_BARRIER 标识,在执行时等待队列中的任务执行完成。
DISPATCH_NOINLINE
void
dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc_cacheonly();
uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_BARRIER;
dispatch_qos_t qos;
if (likely(!dc)) {
return _dispatch_async_f_slow(dq, ctxt, func, 0, dc_flags);
}
qos = _dispatch_continuation_init_f(dc, dq, ctxt, func, 0, dc_flags);
_dispatch_continuation_async(dq, dc, qos, dc_flags);
}