内容:
在 C++17 标准中,异步编程得到了进一步完善。除了 std::async 和 std::future 的基本用法之外,还有许多细节值得深入探讨,例如任务异常传递、协程与异步的结合、以及与线程池的自定义实现。本文将通过一个完整的示例,展示如何构建一个可复用的异步任务框架,支持多种执行策略、错误处理以及结果组合。
-
基本概念回顾
std::async:启动一个异步任务,返回std::future。std::future:提供get()与wait()接口,用于同步获取结果。- 执行策略:
std::launch::async(立即在新线程中执行)与std::launch::deferred(延迟执行)。
-
任务包装器
template<typename Func, typename... Args> auto make_async(Func&& f, Args&&... args) { using Ret = std::invoke_result_t<Func, Args...>; return std::async(std::launch::async, std::forward <Func>(f), std::forward <Args>(args)...); }该包装器保证总是以异步方式执行,避免
deferred的懒执行导致的隐藏线程。 -
异常转发
std::future::get()会在调用时抛出异步任务中捕获的异常。我们可以在外层捕获并记录,或自定义异常类型。 -
结果组合
当需要等待多个异步结果时,可以使用std::future_status与std::wait_for。template<typename Future1, typename Future2> auto join(Future1&& f1, Future2&& f2) { struct TupleResult { std::decay_t<decltype(f1.get())> first; std::decay_t<decltype(f2.get())> second; }; return std::async(std::launch::async, [=] { return TupleResult{f1.get(), f2.get()}; }); } -
与协程的结合
C++20 引入了co_await与std::future的适配器。可以使用std::experimental::make_ready_future或自定义awaiter,实现协程等待异步结果。 -
自定义线程池
std::async每次调用都会创建新线程,资源消耗高。实现一个简易线程池后,可将异步任务提交到固定线程组。class ThreadPool { std::vector<std::thread> workers_; std::queue<std::function<void()>> tasks_; std::mutex mtx_; std::condition_variable cv_; bool stop_ = false; public: ThreadPool(size_t n); ~ThreadPool(); template<class F, class... Args> auto submit(F&& f, Args&&... args) -> std::future<decltype(f(args...))>; };在
submit中使用std::packaged_task包装函数,并将其推入任务队列。 -
完整示例
int heavyComputation(int x) { /* ... */ } double readSensor() { /* ... */ } int main() { auto pool = std::make_shared <ThreadPool>(4); auto fut1 = pool->submit(heavyComputation, 42); auto fut2 = pool->submit(readSensor); try { auto [res1, res2] = join(fut1, fut2).get(); std::cout << "Computation: " << res1 << ", Sensor: " << res2 << '\n'; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << '\n'; } } -
总结
std::async与std::future为 C++ 提供了直观的异步接口。- 通过包装器和自定义线程池,可以显著提升性能与可维护性。
- 结合 C++20 协程,可实现更简洁的异步代码流。
通过上述技术栈,开发者可以在现代 C++ 项目中实现高效、可读、可维护的异步逻辑。