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

异步代码轮询取消令牌与注册取消回调的核心差异及实现疑问

C++异步取消:轮询stop_token与注册回调的疑问解答

背景

我正在观看《泛型异步编程:C++执行器详解》(第1、2部分),内容围绕P2300提案展开。关于异步任务的取消支持,Eric Niebler给出了标准流程:

  • 调用方声明std::stop_source,调用其get_token()方法生成std::stop_token并传递给异步代码
  • 调用方通过std::stop_source的request_stop()方法发起取消请求

异步代码需要响应取消请求,有两种实现方式:

  1. 定期轮询std::stop_token的stop_requested()方法,判断是否需要终止任务
  2. 注册回调函数,在取消请求发起时自动执行

疑问与解答

1. 注册取消回调的具体实现是怎样的?

你可以通过std::stop_token的register_callback()方法注册回调。这个方法接收一个可调用对象(比如lambda、函数指针),当request_stop()被调用时,所有注册的回调会被触发执行。

示例代码:

void async_task(std::stop_token st) {
    // 注册取消回调
    auto callback_handle = st.register_callback([](){
        // 这里写取消触发时的逻辑:比如清理资源、设置终止标志等
        std::cout << "取消请求已触发,执行回调逻辑" << std::endl;
    });

    // 执行异步任务的核心逻辑
    while(!st.stop_requested()) {
        // 模拟任务执行
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

注意:register_callback()返回的std::stop_callback对象需要保持有效,直到回调被执行或令牌被销毁——如果这个对象提前被销毁,回调会被自动取消注册。

2. 两种取消响应方式的核心差异是什么?轮询是否不可避免?

两种方式的核心差异在于取消触发的主动性和轮询的责任方:

  • 轮询方式:由异步任务自身主动定期检查取消状态,轮询逻辑完全在业务代码中。这种方式优势是灵活,你可以控制检查时机(比如任务的关键节点),但缺点是如果任务进入长时间阻塞(比如等待IO、锁),可能无法及时响应取消。
  • 回调方式:轮询的责任转移到了标准库的stop_source/stop_token实现内部。当调用request_stop()时,标准库会直接触发所有注册的回调——这里的触发不需要业务代码轮询,而是由取消请求的发起方线程(或标准库内部机制)执行回调。

你提到的“轮询发生的位置导致差异”是对的:回调方式下,状态监听是标准库内部实现的,业务代码无需关心;轮询方式下,业务代码必须自行实现状态检查逻辑。

3. 轮询取消令牌与注册取消回调的根本差异是什么?

根本差异在于响应取消的时机控制模型:

  • 轮询是**拉取式(Pull)**模型:异步任务主动从stop_token拉取取消状态,决定何时响应。适合任务有明确循环或检查节点的场景,比如批量处理任务。
  • 回调是**推送式(Push)**模型:取消事件被主动推送给异步任务,回调会在取消请求发起时立即执行。适合需要立即响应取消的场景,比如任务可能进入长时间阻塞,或者需要快速清理资源的情况。

另外,回调方式可以在取消发生时立即执行操作,不需要等待任务的下一个检查点;而轮询方式只能在任务的下一次检查时发现取消请求,响应延迟取决于轮询频率。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 17:23:20