美文网首页收藏
GCD ⑧ Dispatch Source

GCD ⑧ Dispatch Source

作者: _涼城 | 来源:发表于2022-04-16 10:43 被阅读0次

Dispatch Source

    GCD 中除了主要的 Disaptch Queue 外,还有不太引人注目的 Dispatch Source。它是 BSD 系统内核功能 kqueue 的包装,其 CPU 内核非常小,尽量不占用资源。

Dispatch Source 可处理以下事件:

名称 内容
DISPATCH_SOURCE_TYPE_DATA_ADD 变量增加
DISPATCH_SOURCE_TYPE_DATA_OR 变量 OR
DISPATCH_SOURCE_TYPE_DATA_REPLACE 变量替换
DISPATCH_SOURCE_TYPE_MACH_SEND MACH 端口发送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH 端口接收
DISPATCH_SOURCE_TYPE_PROC 检测到与进程相关的事件
DISPATCH_SOURCE_TYPE_READ 可读取文件映像
DISPATCH_SOURCE_TYPE_SIGNAL 接收信号
DISPATCH_SOURCE_TYPE_TIMER 定时器
DISPATCH_SOURCE_TYPE_VNODE 文件系统有变更
DISPATCH_SOURCE_TYPE_WRITE 可写入文件影响

DISPATCH_SOURCE_TYPE_TIMER

定时器的例子: 10 秒后执行,执行完后取消,允许延迟 1s,代码如下;

dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 10 *NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1 *NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
    NSLog(@"10s 后");
    dispatch_source_cancel(timer);
});
    
dispatch_source_set_cancel_handler(timer, ^{
    NSLog(@"canceled");
    
});

dispatch_resume(timer);

dispatch_after

    经常会有这样的情况,想在 x 秒后执行处理。在这种想在指定时间后执行处理的情况,可使用 dispatch_after 函数来实现。
在 3 秒后将指定的 Block 追加到 Main Dispatch Queue 中的源代码如下:

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(),^{
    NSLog(^"waited at least three seconds.M);
});

需要注意的是,dispatch_after 函数并不是在指定时间后执行姓理,而只是在指定时间追加处理到 Dispatch Queue 。此源代码与在 3 秒后用dispatch async 函数追加 BlockMain Dispatch Queue 的相同。

    dispatch_after 第一个参数是指定时间用的 dispatch_time_t 类型的值。该值使用 dispatch_time 函数或 dispatch_walltime 函数生成。dispatch_time 函数能够获取从第一个参数 dispatch_time_t 类型值中指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。第一个参数经常使用的值是之前源代码中出现的 DISPATCH_TIME_NOW 这表示现在的时间。即以下源代码可得到表示从现在开始 1 秒后的 dispatch_time_t 类型的值。

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);

dispatch_after 实现

    可以看到 dispatch_after 其实就是通过 Dispatch Source 实现的延迟执行 block 任务

void
dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *ctxt,
  dispatch_function_t func)
{
 _dispatch_after(when, queue, ctxt, func, false);
}

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_after(dispatch_time_t when, dispatch_queue_t dq,
  void *ctxt, void *handler, bool block)
{
 dispatch_timer_source_refs_t dt;
 dispatch_source_t ds;
 uint64_t leeway, delta;

 if (when == DISPATCH_TIME_FOREVER) {
#if DISPATCH_DEBUG
  DISPATCH_CLIENT_CRASH(0, "dispatch_after called with 'when' == infinity");
#endif
  return;
 }

 delta = _dispatch_timeout(when);
 if (delta == 0) {
  if (block) {
   return dispatch_async(dq, handler);
  }
  return dispatch_async_f(dq, ctxt, handler);
 }
 leeway = delta / 10; // <rdar://problem/13447496>

 if (leeway < NSEC_PER_MSEC) leeway = NSEC_PER_MSEC;
 if (leeway > 60 * NSEC_PER_SEC) leeway = 60 * NSEC_PER_SEC;

 // this function can and should be optimized to not use a dispatch source
 ds = dispatch_source_create(&_dispatch_source_type_after, 0, 0, dq);
 dt = ds->ds_timer_refs;

 dispatch_continuation_t dc = _dispatch_continuation_alloc();
 if (block) {
  _dispatch_continuation_init(dc, dq, handler, 0, 0);
 } else {
  _dispatch_continuation_init_f(dc, dq, ctxt, handler, 0, 0);
 }
 // reference `ds` so that it doesn't show up as a leak
 dc->dc_data = ds;
 _dispatch_trace_item_push(dq, dc);
 os_atomic_store2o(dt, ds_handler[DS_EVENT_HANDLER], dc, relaxed);

 dispatch_clock_t clock;
 uint64_t target;
 _dispatch_time_to_clock_and_value(when, false, &clock, &target);
 if (clock != DISPATCH_CLOCK_WALL) {
  leeway = _dispatch_time_nano2mach(leeway);
 }
 dt->du_timer_flags |= _dispatch_timer_flags_from_clock(clock);
 dt->dt_timer.target = target;
 dt->dt_timer.interval = UINT64_MAX;
 dt->dt_timer.deadline = target + leeway;
 dispatch_activate(ds);
}

相关文章

网友评论

    本文标题:GCD ⑧ Dispatch Source

    本文链接:https://www.haomeiwen.com/subject/vvncertx.html