C++ 第三阶段 并发与异步 - 第二节:异步任务(std::async)
目录
一、std::async 概述
1. std::async 的定义
二、std::async 的基本用法
1. 基本语法
(1) 函数调用
(2) Lambda 表达式
三、执行策略详解
1. std::launch::async
2. std::launch::deferred
3. 默认策略(std::launch::any)
四、std::future:异步结果管理
1. 获取结果的方法
五、异常处理与安全性
1. 异步任务中的异常
2. 避免资源泄漏
六、最佳实践
1. 显式指定执行策略
2. 合理使用超时机制
3. 避免共享状态竞争
七、常见问题与解决方案
1. 任务未执行
2. 性能问题
3. 异常未捕获
八、示例代码汇总
示例 1:基础异步任务
示例 2:超时等待
示例 3:多异步任务并行
九、总结
1. std::async 的核心优势
2. 适用场景
3. 后续学习方向
C++从入门到入土学习导航_c++学习进程-CSDN博客
一、std::async
概述
1. std::async
的定义
std::async
是 C++11 引入的高级异步任务管理工具,用于启动异步任务并返回一个 std::future
对象。其核心功能包括:
- 自动管理线程:根据策略决定是否立即创建新线程或延迟执行。
- 异步任务结果获取:通过
std::future
提供阻塞或非阻塞方式获取结果。 - 灵活的执行策略:支持
std::launch::async
(立即执行)和std::launch::deferred
(延迟执行)。
二、std::async
的基本用法
1. 基本语法
template<class Function, class... Args>
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
async(Function&& f, Args&&... args);
(1) 函数调用
#include <iostream>
#include <future>int computeSum(int a, int b) {return a + b;
}int main() {auto future = std::async(computeSum, 3, 4); // 默认策略std::cout << "Result: " << future.get() << std::endl; // 阻塞直到结果可用return 0;
}
(2) Lambda 表达式
auto future = std::async([]() {return "Hello, async!";
});
std::cout << future.get() << std::endl;
三、执行策略详解
1. std::launch::async
- 立即启动新线程:任务在独立线程中执行。
- 适用场景:需要并行执行耗时操作(如计算、I/O)。
- 示例:
auto future = std::async(std::launch::async, []() {std::this_thread::sleep_for(std::chrono::seconds(2));return 42; });
2. std::launch::deferred
- 延迟执行:任务在调用
get()
或wait()
时执行(可能在当前线程)。 - 适用场景:轻量级任务或无需并行的场景。
- 示例:
auto future = std::async(std::launch::deferred, []() {return "Deferred task"; }); std::cout << future.get() << std::endl; // 此时任务执行
3. 默认策略(std::launch::any
)
- 由实现决定:可能选择异步或延迟策略(具体行为依赖编译器和平台)。
- 示例:
auto future = std::async([]() { return 100; }); // 实现决定策略
四、std::future
:异步结果管理
1. 获取结果的方法
-
get()
:阻塞当前线程,直到任务完成并返回结果。注意:get()
只能调用一次。auto future = std::async([]() { return 5 * 5; }); std::cout << "Result: " << future.get() << std::endl;
-
wait()
:阻塞直到任务完成(不返回结果)。auto future = std::async([]() { std::this_thread::sleep_for(1s); }); future.wait(); // 等待任务完成
-
wait_for()
/wait_until()
:带超时的等待。auto future = std::async([]() { std::this_thread::sleep_for(2s); }); auto status = future.wait_for(1s); // 超时检查 if (status == std::future_status::ready) {std::cout << "Task completed!" << std::endl; } else {std::cout << "Timeout!" << std::endl; }
五、异常处理与安全性
1. 异步任务中的异常
- 如果异步任务抛出异常,调用
future.get()
时会重新抛出该异常。 - 示例:
auto future = std::async([]() {throw std::runtime_error("Async error!"); }); try {future.get(); // 抛出异常 } catch (const std::exception& e) {std::cerr << "Caught: " << e.what() << std::endl; }
2. 避免资源泄漏
- 必须调用
get()
或wait()
:未调用会导致线程资源未释放。 - 示例:
{auto future = std::async([]() { return 100; });future.get(); // 必须调用 }
六、最佳实践
1. 显式指定执行策略
- 避免默认策略的不确定性:优先使用
std::launch::async
。auto future = std::async(std::launch::async, heavyComputation, args...);
2. 合理使用超时机制
- 防止无限阻塞:结合
wait_for()
或wait_until()
。auto future = std::async(std::launch::async, longRunningTask); if (future.wait_for(5s) == std::future_status::ready) {processResult(future.get()); } else {std::cerr << "Task timeout!" << std::endl; }
3. 避免共享状态竞争
- 使用锁或原子类型:保护共享数据。
std::mutex mtx; auto task1 = std::async([&]() {std::lock_guard<std::mutex> lock(mtx);sharedData++; });
七、常见问题与解决方案
1. 任务未执行
- 原因:使用
std::launch::deferred
但未调用get()
或wait()
。 - 解决方案:显式调用
get()
。auto future = std::async(std::launch::deferred, []() { return 10; }); future.get(); // 必须调用
2. 性能问题
- 原因:频繁创建短生命周期线程。
- 解决方案:使用线程池(需自行实现)。
// 示例:伪代码,实际需实现线程池 class ThreadPool { public:// 构造函数:启动指定数量的工作线程ThreadPool(size_t numThreads) : stop(false) {for (size_t i = 0; i < numThreads; ++i) {workers.emplace_back([this] { this->worker(); });}}// 析构函数:停止所有线程,确保资源释放~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (std::thread& worker : workers) {if (worker.joinable()) {worker.join();}}}// 提交任务:支持任意可调用对象 + 参数,返回 future 结果template<typename F, typename... Args>auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));{std::unique_lock<std::mutex> lock(queue_mutex);tasks.push([task]() { (*task)(); });}condition.notify_one();return task->get_future();}private:// 工作线程主循环:等待任务并执行void worker() {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queue_mutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) return;task = std::move(tasks.front());tasks.pop();}task();}}std::vector<std::thread> workers; // 工作线程池std::queue<std::function<void()>> tasks; // 任务队列std::mutex queue_mutex; // 保护任务队列的互斥锁std::condition_variable condition; // 同步任务队列的条件变量bool stop; // 是否停止线程池 };
3. 异常未捕获
- 原因:未处理异步任务的异常。
- 解决方案:在
get()
调用时捕获异常。try {future.get(); } catch (...) {std::cerr << "Unexpected error!" << std::endl; }
八、示例代码汇总
示例 1:基础异步任务
#include <iostream>
#include <future>
#include <thread>int computeSquare(int x) {return x * x;
}int main() {auto future = std::async(std::launch::async, computeSquare, 5);std::cout << "Computing square..." << std::endl;std::cout << "Result: " << future.get() << std::endl;return 0;
}
示例 2:超时等待
#include <iostream>
#include <future>
#include <chrono>void longRunningTask() {std::this_thread::sleep_for(std::chrono::seconds(3));
}int main() {auto future = std::async(std::launch::async, longRunningTask);std::cout << "Waiting for task..." << std::endl;auto status = future.wait_for(std::chrono::seconds(2));if (status == std::future_status::ready) {std::cout << "Task completed!" << std::endl;} else {std::cout << "Task not ready yet!" << std::endl;}return 0;
}
示例 3:多异步任务并行
#include <iostream>
#include <future>
#include <vector>
#include <thread>int computeSum(int a, int b) {return a + b;
}int main() {std::vector<std::future<int>> futures;for (int i = 0; i < 5; ++i) {futures.push_back(std::async(std::launch::async, computeSum, i, i * 2));}for (auto& future : futures) {std::cout << "Result: " << future.get() << std::endl;}return 0;
}
九、总结
1. std::async
的核心优势
- 简化线程管理:自动处理线程创建和销毁。
- 灵活的执行策略:支持异步和延迟两种模式。
- 结果安全获取:通过
std::future
提供阻塞和非阻塞接口。
2. 适用场景
- 后台任务:如文件读写、网络请求、计算密集型任务。
- 并行处理:同时执行多个独立任务。
- 异步结果获取:避免主线程阻塞,提升程序响应性。
3. 后续学习方向
- 高级同步机制:如
std::condition_variable
、std::atomic
。 - 线程池实现:优化
std::async
的性能瓶颈。 - 异步任务链:结合
std::future
和std::promise
实现复杂任务依赖。
通过掌握 std::async
,开发者可以高效管理异步任务,提升程序的并发性能和响应能力,为构建复杂的多线程应用打下坚实基础。