C++stop_requested是否已请求停止

2026-03-22 18:30:30 1468阅读

C++20 stop_requested():如何可靠检测停止请求

在现代C++并发编程中,std::jthread 与协作式取消机制(cooperative cancellation)的引入,标志着线程生命周期管理进入了一个更安全、更可控的新阶段。其中,stop_requested()std::stop_sourcestd::stop_tokenstd::stop_callback 协同工作体系中的核心查询接口。它用于判断当前是否已触发停止请求——但需注意:函数仅反映“请求是否发出”,并不保证执行已终止或资源已完成清理。本文将系统解析其语义、使用场景、常见误区及典型实践模式。

语义本质:轻量级状态快照

stop_requested() 是一个无副作用的 const 成员函数,返回 bool 类型。它不阻塞、不等待、不修改任何内部状态,仅原子地读取底层停止状态位。该调用通常被编译为单条内存加载指令(如 load acquire),具备极低开销,适合高频轮询。

关键在于理解其“请求”(request)与“响应”(response)的分离性:

  • 调用 std::stop_source::request_stop() 后,所有关联的 std::stop_tokenstop_requested() 将立即(或极短时间内)返回 true
  • 线程是否真正停止、何时停止、是否完成清理,完全由用户代码决定;
  • stop_requested() 本身不会引发异常,也不会自动中断循环或释放资源。

基础用法示例

以下是一个标准的带停止检查的后台任务实现:

#include <thread>
#include <stop_token>
#include <chrono>
#include <iostream>

void background_task(std::stop_token stoken) {
    int counter = 0;
    while (!stoken.stop_requested()) {
        // 模拟工作:每秒打印一次计数
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout << "Working... " << ++counter << "\n";

        // 可选:在耗时操作中插入检查点
        if (stoken.stop_requested()) {
            break;
        }
    }
    std::cout << "Task stopped gracefully.\n";
}

int main() {
    std::jthread t{background_task};
    std::this_thread::sleep_for(std::chrono::seconds(3));
    // 主动请求停止
    t.request_stop();
    // jthread 自动 join,无需显式调用
}

此处 stop_requested() 位于循环条件中,确保每次迭代前检查状态。若工作逻辑较长(如文件读写、网络等待),应在关键子步骤后再次检查,避免响应延迟。

stop_possible() 的区别

std::stop_token 还提供 stop_possible() 方法,用于判断该 token 是否可能接收停止请求(即是否关联有效 stop_source)。它常用于防御性编程:

void safe_worker(std::stop_token stoken) {
    // 若 token 不可停止,则跳过检查逻辑
    if (!stoken.stop_possible()) {
        do_infinite_work(); // 无取消能力的任务
        return;
    }

    while (!stoken.stop_requested()) {
        do_some_work();
    }
}

注意:stop_possible()true 并不意味着 stop_requested() 当前为 true;二者正交——前者是能力声明,后者是状态快照。

线程安全与内存序保障

stop_requested() 内部采用 std::memory_order_acquire 语义读取状态。这意味着:

  • 它能同步获取此前由 request_stop()(使用 release 序)写入的停止标记;
  • 同时,它可确保其后的读写操作不会被重排至该调用之前,从而保护临界区数据一致性。

例如,在清理资源前确保看到停止信号:

struct ResourceHolder {
    std::mutex mtx;
    std::vector<int> data;

    void process_with_cleanup(std::stop_token stoken) {
        while (!stoken.stop_requested()) {
            // ... 工作逻辑
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }

        // 此处 stop_requested() 为 true,且 acquire 语义保证:
        // 以下临界区操作不会被重排到检查之前
        std::lock_guard<std::mutex> lock{mtx};
        data.clear(); // 安全清理
    }
};

常见误用警示

  1. 忽略检查点密度:在长周期计算中仅在循环头检查,导致响应延迟数秒甚至更久;
  2. 混淆 stop_requested() 与线程存活状态:该函数返回 true 后,线程仍可能运行数毫秒,不可假设立即退出;
  3. 析构函数中依赖 stop_requested():若 jthreadjoin()detach(),其 stop_token 可能失效,此时调用未定义行为;
  4. std::stop_callback 的误解:回调仅在 request_stop() 时触发一次,不替代主动轮询。

结语:协作优于强制

stop_requested() 并非“强制终止开关”,而是协作契约中的关键信号探针。它的价值不在于单次调用,而在于嵌入整个任务生命周期的设计哲学:明确检查点、及时响应、有序清理。在 std::jthread 与结构化并发模型下,合理使用该接口,可显著提升多线程程序的鲁棒性与可维护性。记住,真正的停止从来不是由系统强加,而是由开发者以清晰逻辑主动达成——stop_requested(),正是这一逻辑链条上最轻量也最可靠的基石。

文章版权声明:除非注明,否则均为Dark零点博客原创文章,转载或复制请以超链接形式并注明出处。

目录[+]