1.1 需求场景与基本原理
在复杂的应用中,异步执行可以把耗时的任务放到后台,避免阻塞主线程,从而提升应用的响应性。使用 std::future 与 std::promise 可以实现任务的结果传递、错误传播与线程间通信,降低耦合度。
通过 Promise-Future 模型,生产者可以在完成工作后设置结果,而消费者则通过未来对象获取该结果。这样做的关键点是保证对结果的访问发生在任务完成之后,并在需要时进行异常处理。
#include <iostream>
#include <thread>
#include <future>
#include <chrono>int main() {std::promise p;std::future f = p.get_future();std::thread th([&p]() {std::this_thread::sleep_for(std::chrono::milliseconds(200));p.set_value(42); // 将结果传递给消费者});int result = f.get(); // 阻塞等待结果std::cout << "结果: " << result << std::endl;th.join();return 0;
}
异常传递也是设计要点之一,通过 set_exception 可以把异常从生产者传递到消费者侧,消费者在 get() 时捕获并处理。
#include <iostream>
#include <thread>
#include <future>
#include <stdexcept>int main() {std::promise p;std::future f = p.get_future();std::thread th([&p]() {try {throw std::runtime_error("工作失败");} catch (...) {p.set_exception(std::current_exception());}});try {int v = f.get();std::cout << v << std::endl;} catch (const std::exception& e) {std::cout << "捕获异常: " << e.what() << std::endl;}th.join();return 0;
}
2.1 std::future 与 std::promise 的基本用法
2.1.1 基本创建与同步获取
std::promise 提供一个能被设置的结果点,std::future 则暴露一个读取该结果的入口。通过在另一个线程中对 Promise 调用 set_value,主线程通过 get() 获取结果,实现简单的事件驱动通信。
#include <iostream>
#include <thread>
#include <future>int main() {std::promise p;std::future f = p.get_future();std::thread t([&p]() {// 模拟工作负载std::this_thread::sleep_for(std::chrono::milliseconds(100));p.set_value(25);});int v = f.get();std::cout << "得到的结果: " << v << std::endl;t.join();return 0;
}
wait() 或者推广的 wait_for()、wait_until() 可以在需要时控制等待行为,避免无限阻塞。
#include <iostream>
#include <thread>
#include <future>
#include <chrono>int main() {std::promise p;std::future f = p.get_future();std::thread t([&p]() {std::this_thread::sleep_for(std::chrono::milliseconds(300));p.set_value(7);});if (f.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready) {std::cout << "提前拿到: " << f.get() << std::endl;} else {std::cout << "等待超时,后续再取" << std::endl;// 之后再获取std::cout << "最终结果: " << f.get() << std::endl;}t.join();return 0;
}
2.1.2 基本的异常传递
如果在异步工作中抛出异常,消费者端通过 f.get() 捕获异常并处理。通过 set_exception 将异常传递回消费者,使错误处理可以集中在调用端完成。
#include <iostream>
#include <thread>
#include <future>
#include <exception>int main() {std::promise p;std::future f = p.get_future();std::thread t([&p]() {try {throw std::invalid_argument("输入无效");} catch (...) {p.set_exception(std::current_exception());}});try {f.get();} catch (const std::exception& ex) {std::cout << "异常捕获: " << ex.what() << std::endl;}t.join();return 0;
}
3.1 使用 std::async 实现异步任务
3.1.1 基于 std::async 的任务调度
std::async 提供一种更高层级的异步执行入口,底层仍然借助 future/ promise 机制来传递结果。通过指定 std::launch::async,可以强制在新线程中执行,避免阻塞调用方。
#include <iostream>
#include <thread>
#include <future>
#include <chrono>int main() {auto fut = std::async(std::launch::async, []() {std::this_thread::sleep_for(std::chrono::milliseconds(100));return 123;});// 其他工作std::cout << "正在执行其他任务..." << std::endl;int res = fut.get();std::cout << "异步任务结果: " << res << std::endl;return 0;
}
3.1.2 使用 std::packaged_task 做自定义任务包装
若需要把任意可调用对象包装成一个可独立执行并输出未来结果的任务,可以使用 std::packaged_task,它与 std::future 一一对应,便于控流和复用。
#include <future>
#include <iostream>
#include <thread>
#include <chrono>int main() {std::packaged_task task([]() {std::this_thread::sleep_for(std::chrono::milliseconds(50));return 77;});std::future f = task.get_future();std::thread t(std::move(task));std::cout << "任务结果: " << f.get() << std::endl;t.join();return 0;
}
4.1 线程间通信的最佳实践
4.1.1 使用等待策略避免死锁与长期阻塞
在多线程场景下,尽量避免长期阻塞 on get(),优先使用 wait_for、wait_until 来实现超时控制,必要时再进行释放资源与清理。
#include <future>
#include <iostream>
#include <thread>
#include <chrono>int main() {std::promise p;std::future f = p.get_future();std::thread t([&p]() {std::this_thread::sleep_for(std::chrono::milliseconds(500));p.set_value(42);});if (f.wait_for(std::chrono::milliseconds(300)) == std::future_status::ready) {std::cout << "结果: " << f.get() << std::endl;} else {std::cout << "等待超时" << std::endl;}t.join();return 0;
}
4.1.2 使用 shared_future 实现多消费者读取同一结果
如果需要让多个消费者读取同一个任务的结果,可以将未来对象转换为 std::shared_future,避免多次阻塞。请注意共享状态的并发读取不会改变结果。
#include <future>
#include <iostream>int main() {std::promise p;std::shared_future sf = p.get_future().share();std::thread t([&p]() {p.set_value(99);});std::cout << "消费者1: " << sf.get() << std::endl;std::cout << "消费者2: " << sf.get() << std::endl;t.join();return 0;
}
5.1 生产者-消费者场景中的异步通信
5.1.1 基于 promise/future 的简单生产者-消费者模型
在一个简化的模型中,生产者为每个任务创建一个独立的 std::promise,消费者通过对应的 std::future 获取结果。这种模式适用于需要对每个任务单独跟踪结果的场景,且能清晰地分离生产者与消费者。
#include <iostream>
#include <thread>
#include <future>
#include <vector>
#include <chrono>int main() {std::vector> futures;// 生产者:为每个任务创建一个 Promise/Future 对for (int i = 0; i < 5; ++i) {std::promise *p = new std::promise();futures.push_back(p->get_future());// 用一个独立的线程来完成任务并设置结果std::thread([p, i]() {std::this_thread::sleep_for(std::chrono::milliseconds(50 * (i + 1)));p->set_value(i * 10);delete p;}).detach();}// 消费者:逐个获取并处理结果for (auto &f : futures) {std::cout << "消费结果: " << f.get() << std::endl;}return 0;
}
在上述实现中,每个任务都有一个独立的 Promise,通过 get_future() 将未来结果暴露给消费者。虽然实现简单,但在任务数量较大时,需要注意资源管理与线程开销。
5.1.2 最佳实践要点
综合使用场景时,遵循以下要点可以提升实现质量:优先使用 std::async 或 Packaged Task 来实现任务的异步执行,尽量减少手动管理 Promise 的数量;在共享结果时使用 shared_future;对可能阻塞的获取操作设置合理的超时与取消策略;对异常要有明确的传播路径。

// 简要对比:使用 std::async 更易扩展,直观地返回 future
#include <iostream>
#include <future>int main() {auto fut = std::async(std::launch::async, []() {// 模拟耗时return 58;});std::cout << "异步结果: " << fut.get() << std::endl;return 0;
} 

