C++异步编程
C++异步编程指南:async与future详解 本文系统介绍了C++中的异步编程机制,重点解析std::async和std::future的使用方法。首先通过同步/异步、阻塞/非阻塞的概念对比,以及生活化的奶茶店示例,帮助理解四种组合模式。然后详细讲解std::async的两种启动策略(async和deferred)和std::future的三种状态检测方法,包括wait()、get()和wai
一、异步任务基础
1.1、厘清概念
同步 vs 异步(关注通信机制)
- 同步:调用者主动等待被调用者返回的结果
- 异步:调用者不等待结果,被调用者完成后通过回调方式通知调用者
阻塞 vs 非阻塞(关注等待状态): - 阻塞:调用者等待被调用者的结果,直到返回,期间不能做其他事情
- 非阻塞:调用者不等待被调用者的结果,可以继续做其他事情
1.2、四种组合模式
通过生活中点奶茶的例子,来进行理解
| 模式 | 现实场景 | 技术特点 |
|---|---|---|
| 同步阻塞 | 去奶茶店点奶茶,拿到号之后,就一直站在柜台前等待奶茶的到来,期间不能做其他事情 | 线程挂起,主动等待结果 |
| 同步非阻塞 | 去奶茶店点奶茶,拿到号之后,可以做其他事情(刷刷短视频,看看朋友圈等),但是每隔一段时间就去询问奶茶是否做好了 | 线程运行,主动轮询结果 |
| 异步阻塞 | 去奶茶店点奶茶,拿到号之后,坐在店里的等待区,奶茶做好了之后,店员会叫号通知你来取奶茶 | |
| 异步非阻塞 | 去奶茶店点奶茶,拿到号之后,可以去附近逛下,奶茶做好了之后,店员会打电话通知你来取奶茶 | 线程运行,被动等待通知 |
1.3、异步任务
异步任务,就是一个任务独立于主线程,主线程无需等待任务完成便可继续执行其他任务。当异步任务完成之后,可通过某种机制来获取异步任务的结果。-------异步非阻塞
之前在TrinityCore的异步连接池中,就大量的使用异步任务
在c++中,是通过std::async和std::future来实现的。
二、async与future
2.1、std::async 启动策略
std::async是C++11中引入的一个函数,用于启动一个异步任务。
std::future<T> std::async(std::launch policy, F&& func, Args&&... args);
启动策略:
- std::launch::async:强制在新线程中异步执行
- std::launch::deferred:延迟执行,直到调用get()或wait()时才在当前线程执行
注意: 如果不显式指定策略,系统会根据实现选择默认策略,可能导致不同平台行为不一致。
2.2、std::future 结果获取
std::future是C++11中引入的一个类,主要用于获取异步任务的结果。
template<class T>
class std::future
{
public:
void wait() const; // 阻塞直到任务完成
T get(); // 获取结果(可能阻塞)
// ... 其他成员函数
};
关键方法
wait():- 阻塞等待异步任务的结束
- 不关系异步任务的结果,只关心是否完成;换句话说就是用于
没有返回值的异步任务,不会抛出异常
get():- 存在两个返回值,一个是异常,另一个是异步任务的结果
- 一般需要使用
try catch来捕获异常
2.3、示例代码
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
// 有返回值的异步任务
int multiplyTask(const int& num)
{
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
return num * 3;
}
// 无返回值的异步任务
void printTask() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Print task completed!" << std::endl;
}
int main()
{
int input = 20;
// 示例1:有返回值的异步任务
{
std::future<int> result = std::async(std::launch::async, multiplyTask, input);
try {
int value = result.get(); // 阻塞直到结果可用
std::cout << "Result: " << value << std::endl;
} catch (const std::exception& e) {
std::cout << "Exception caught: " << e.what() << std::endl;
}
}
// 示例2:无返回值的异步任务
{
std::future<void> result = std::async(std::launch::async, printTask);
result.wait(); // 只等待完成,不获取返回值
std::cout << "Print task finished waiting." << std::endl;
}
return 0;
}
2.4、非阻塞获得异步任务结果
#include <iostream>
#include <future>
#include <chrono>
int longRunningTask()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
return 42;
}
int main()
{
auto future = std::async(std::launch::async, longRunningTask);
// 使用 wait_for 进行超时检查
while (true) {
auto status = future.wait_for(std::chrono::milliseconds(500));
if (status == std::future_status::ready) {
std::cout << "Task completed! Result: " << future.get() << std::endl;
break;
} else if (status == std::future_status::timeout) {
std::cout << "Task still running, doing other work..." << std::endl;
// 执行其他工作
} else if (status == std::future_status::deferred) {
std::cout << "Task is deferred" << std::endl;
break;
}
}
return 0;
}
wait_for(duration):等待指定时长,返回任务状态wait_until(time_point):等待到指定时间点,返回任务状态
返回值说明:ready:任务已完成,结果可用timeout:超时,任务仍在运行deferred:任务被延迟(仅使用deferred策略时)
2.5、延迟任务
// 延迟执行示例
auto deferredFuture = std::async(std::launch::deferred, []() {
std::cout << "This task runs only when get() or wait() is called" << std::endl;
return 100;
});
// 此时任务还未执行
std::cout << "Doing some work..." << std::endl;
// 现在才真正执行任务
int result = deferredFuture.get(); // 任务在此处执行
适用场景:
- 系统负载较高时推迟非紧急任务
- 结果不立即需要但最终需要获取的任务
- 避免不必要的线程创建开销
2.6、共享future
#include <iostream>
#include <future>
#include <vector>
#include <thread>
int computeValue()
{
std::this_thread::sleep_for(std::chrono::seconds(2));
return 42;
}
int main() {
// 创建异步任务
std::future<int> fut = std::async(std::launch::async, computeValue);
// 转换为 shared_future 供多个消费者使用
std::shared_future<int> shared_fut = fut.share();
// 多个线程可以安全地访问结果
std::vector<std::thread> threads;
for (int i = 0; i < 3; ++i) {
threads.emplace_back([i, shared_fut]() {
int result = shared_fut.get(); // 所有线程获取相同结果
std::cout << "Thread " << i << " got: " << result << std::endl;
});
}
for (auto& t : threads) {
t.join();
}
return 0;
}
future:适合一次性获取异步执行结果,多次调用get会抛出异常。
shared_future 特点:
- 允许多次调用
get()方法 - 多个消费者可以共享同一个异步结果
- 线程安全,适合多线程环境
三、总结
-
明确指定启动策略,避免平台差异性
-
及时处理异常,使用
try-catch包装 get() 调用 -
合理使用等待机制,根据场景选择阻塞或非阻塞方式
-
多消费者场景使用
shared_future,避免多次创建相同任务 -
注意生命周期管理,确保
future对象在有效期内使用
更多推荐


所有评论(0)