欢迎光临
我们一直在努力

深入理解C++ future/promise/async

C++ 中 future / promise / async 实现多线程详解(含可运行示例)

下面把上一段被截断的内容补完,并给出一个完整可编译运行的例子。


一、心智模型:共享状态(Shared State)

text

promise —[set_value/set_exception]—> 共享状态 <—[get/wait]— future
std::async 则是"自动挡":创建线程 + 建 promise + 跑函数 + 写结果,打包返回 future

future 和 promise 本身不存值,只是"共享状态"的两端句柄。共享状态里放着:

  • 一个原子状态标志(未就绪 / 就绪 / 已取走);

  • 值存储区或异常指针;

  • 完成通知机制(条件变量/futex)。

  • 由此得到几条关键语义:

    • get() 只能调一次,取走后 valid() 变 false;

    • get() 阻塞到就绪,然后返回值或重新抛出存入的异常;

    • wait() 只等不取,之后仍可 get();

    • promise 析构时若未设值,自动写入 broken_promise 异常,避免等待方永久卡死。


    二、std::promise:手工写端

    cpp

    std::promise<int> p;
    std::future<int> f = p.get_future(); // 只能取一次,第二次抛 future_already_retrieved
    std::thread t([p = std::move(p)]() mutable {
    p.set_value(42); // 只能成功一次,否则 promise_already_satisfied
    });
    int v = f.get(); // 阻塞直到 42 就绪
    t.join();

    要点:

    • promise 只能移动,传给线程要 std::move;

    • set_value / set_exception 只能调一次;

    • set_value_at_thread_exit:结果推迟到本线程 TLS 析构完毕后才可见;

    • 异常传递是这套机制的核心价值:

    cpp

    try { throw std::runtime_error("boom"); }
    catch (…) { p.set_exception(std::current_exception()); }
    // 另一端 f.get() 会重新抛出 runtime_error


    三、std::future:读端

    接口说明
    get() 等待+取值(或抛异常),之后失效
    wait() 只等就绪
    wait_for(d) / wait_until(tp) 超时等待,返回 ready / timeout / deferred
    share() 转成 shared_future<T>

    future 是独占的。多个线程都要读时用 std::shared_future<T>(可拷贝、可多次 get()):

    cpp

    std::shared_future<int> sf = p.get_future().share();
    std::thread a([sf]{ std::cout << sf.get(); });
    std::thread b([sf]{ std::cout << sf.get(); });
    p.set_value(99);


    四、std::async:任务级并发

    4.1 三种启动策略

    cpp

    std::async(std::launch::async, f, args…); // 强制新线程,立即开始
    std::async(std::launch::deferred, f, args…); // 不建线程,get/wait 时在调用线程同步执行
    std::async(f, args…); // 默认 = async | deferred,由实现选

    deferred 的行为:任务体被"存起来",直到对 future 调 get() / wait() / wait_for() 才在当前调用线程上同步执行;此时 wait_for 立刻返回 future_status::deferred(表示"还没开始"),而不是 timeout。

    4.2 ⚠️ 最著名的两个陷阱

    陷阱 1:不保存返回值 → 退化成同步

    cpp

    std::async(std::launch::async, []{ do_work(); }); // 临时 future 在本语句末尾析构

    如果 async 返回的 future 是临时对象被立即析构,而它关联的任务尚未完成,标准规定析构会阻塞等待任务结束(相当于隐式 join)。结果这句"异步"实际上是同步的。务必把 future 存进变量。

    陷阱 2:默认策略不确定

    std::async(f, …) 默认可能被实现选成 deferred,从而根本不并发。要保证并发,显式写 std::launch::async。

    // g++ -std=c++17 -pthread main.cpp -o demo
    #include <future>
    #include <thread>
    #include <iostream>
    #include <vector>
    #include <chrono>
    #include <stdexcept>
    #include <numeric>
    #include <string>

    using namespace std::chrono_literals;

    // 模拟耗时计算
    int heavy_square(int x) {
    std::this_thread::sleep_for(300ms);
    return x * x;
    }

    int main() {
    // ———- 1. std::async 并行计算 ———-
    {
    std::vector<std::future<int>> futures;
    auto t0 = std::chrono::steady_clock::now();

    for (int i = 1; i <= 4; ++i)
    futures.push_back(std::async(std::launch::async, heavy_square, i));

    int sum = 0;
    for (auto& f : futures)
    sum += f.get(); // 每个 get 阻塞到各自完成

    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
    std::chrono::steady_clock::now() – t0).count();
    std::cout << "[1] sum = " << sum << " (1+4+9+16=30), 耗时 " << ms
    << " ms(串行应约 1200 ms)\\n";
    }

    // ———- 2. promise/future 手工通信 ———-
    {
    std::promise<std::string> p;
    std::future<std::string> f = p.get_future();

    std::thread t([p = std::move(p)]() mutable {
    std::this_thread::sleep_for(200ms);
    p.set_value("hello from worker thread");
    });

    std::cout << "[2] " << f.get() << "\\n"; // 阻塞直到被 set_value
    t.join();
    }

    // ———- 3. 异常跨线程传递 ———-
    {
    auto f = std::async(std::launch::async, []() -> int {
    throw std::runtime_error("boom inside async");
    return 0; // 不可达
    });
    try {
    (void)f.get(); // 重新抛出 runtime_error
    } catch (const std::exception& e) {
    std::cout << "[3] caught: " << e.what() << "\\n";
    }
    }

    // ———- 4. wait_for 超时轮询 ———-
    {
    auto f = std::async(std::launch::async, [] {
    std::this_thread::sleep_for(500ms);
    return 7;
    });
    while (f.wait_for(100ms) != std::future_status::ready)
    std::cout << "[4] still working…\\n";
    std::cout << "[4] result = " << f.get() << "\\n";
    }

    // ———- 5. shared_future:一个结果多方消费 ———-
    {
    std::promise<int> p;
    std::shared_future<int> sf = p.get_future().share();

    std::thread a([sf]{ std::cout << "[5] A got " << sf.get() << "\\n"; });
    std::thread b([sf]{ std::cout << "[5] B got " << sf.get() << "\\n"; });

    p.set_value(99); // 广播唤醒 A、B
    a.join(); b.join();
    }

    // ———- 6. broken_promise 演示 ———-
    {
    std::future<int> f;
    {
    std::promise<int> p;
    f = p.get_future();
    // p 未 set_value 就析构
    }
    try {
    f.get();
    } catch (const std::future_error& e) {
    std::cout << "[6] future_error: " << e.code().message() << "\\n";
    }
    }

    return 0;
    }

    赞(0)
    未经允许不得转载:171主机测评 » 深入理解C++ future/promise/async
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址