GCD ③ dispatch_barrier

dispatch_barrier (栅栏)

    在访问数据库或文件时,如前所述,使用 Serial Dispatch Queue 可避免数据竟争的问题。
    写入处理确实不可与其他的写入处理以及包含读取处理的其他某些处理并行执行。但是如果读取处理只是与读取处理并行执行,那么多个并行执行就不会发生问题。也就是说,为了高效率地进行访问,读取处理追加到 Concurrent Dispatch Queue 中,写入处理在任一个读取处理没有执行的状态下,追加到 Serial Dispatch Queue 中即可(在写入处理结束之前,读取处理不可执行)。
    虽然利用 Dispatch Groupdispatch_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_async.png

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 函数执行流程如下:

  1. _dispatch_sync_function_invoke_inline 同步执行 block,与 dispatch_sync 一致

  2. 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);
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容