You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

并发队列中DispatchSourceTimer事件与取消回调同步性及线程安全问询

核心结论

  • 你观察到单次定时器触发事件后仍执行取消回调是符合libdispatch设计的:官方文档描述的cancel()会「阻止事件handler后续调用」,指的是阻止还未提交到队列的新事件,已经触发提交、正在队列中等待/已经开始执行的事件handler不会被中断,且取消handler无论事件是否触发都会最终调度执行。
  • 你不能假设cancel()调用和事件handler在业务层面是互斥同步的:libdispatch内部的同步仅保证dispatch source的状态不会出现异常,不会序列化已经提交的两个handler的执行,默认使用全局并发队列的情况下,事件handler和取消handler完全可能在不同线程并行执行。
  • 你当前的DispatchWorkItem写法存在竞态条件,线程不安全:DispatchWorkItem.cancel()仅会标记任务为取消状态,不会中断已经开始执行的任务,会出现取消handler已经启动执行,事件handler才调用cancel()的情况,最终两个回调逻辑都会执行。

安全实现方案

最简单可靠的方案是给定时器指定自定义串行队列作为目标队列,所有回调都会被序列化到该队列执行,天然避免并发竞态,通过布尔状态位即可保证两个回调仅执行一个:

// 自定义串行队列,所有timer回调都派发到该队列执行,保证串行无并发
let timerQueue = DispatchQueue(label: "com.example.timer.serialQueue")
let timer = DispatchSource.makeTimerSource(queue: timerQueue)

// 状态位仅在timerQueue上读写,无需额外加锁
var hasExecuted = false

timer.setEventHandler {
    guard !hasExecuted else { return }
    hasExecuted = true
    // 事件业务逻辑
    print("event handler")
}

timer.setCancelHandler {
    guard !hasExecuted else { return }
    hasExecuted = true
    // 取消业务逻辑
    print("cancel handler")
}

timer.schedule(wallDeadline: .now())
timer.activate()

DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(1)) {
    timer.cancel()
}

如果要继续使用DispatchWorkItem方案,需要同时满足两个条件:

  1. 给timer指定串行队列作为目标队列,序列化两个回调的执行
  2. 在取消任务开头判断取消状态,避免已经被标记取消的任务继续执行
let timerQueue = DispatchQueue(label: "com.example.timer.serialQueue")
let timer = DispatchSource.makeTimerSource(queue: timerQueue)

let cancelHandler = DispatchWorkItem {
    guard !cancelHandler.isCancelled else { return }
    print("cancel handler")
}

timer.setEventHandler {
    cancelHandler.cancel()
    print("event handler")
}

timer.setCancelHandler(handler: cancelHandler)
timer.schedule(wallDeadline: .now())
timer.activate()

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
    timer.cancel()
}

内容的提问来源于stack exchange,提问作者pronebird

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 18:27:00