异步代码轮询取消令牌与注册取消回调的核心差异及实现疑问
C++异步取消:轮询
stop_token与注册回调的疑问解答 背景
我正在观看《泛型异步编程:C++执行器详解》(第1、2部分),内容围绕P2300提案展开。关于异步任务的取消支持,Eric Niebler给出了标准流程:
- 调用方声明
std::stop_source,调用其get_token()方法生成std::stop_token并传递给异步代码 - 调用方通过
std::stop_source的request_stop()方法发起取消请求
异步代码需要响应取消请求,有两种实现方式:
- 定期轮询
std::stop_token的stop_requested()方法,判断是否需要终止任务 - 注册回调函数,在取消请求发起时自动执行
疑问与解答
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
相关产品推荐
相关产品推荐

