C++ 异步编程完全指南
本文档详细介绍C++11引入的异步编程工具:std::future、std::promise、std::packaged_task 和 std::async,帮助你在多线程环境下高效地进行任务管理和结果获取。
目录
- [1. 异步编程基础](#1. 异步编程基础)
- [1.1 什么是异步编程](#1.1 什么是异步编程)
- [1.2 为什么需要异步编程](#1.2 为什么需要异步编程)
- [1.3 核心组件概览](#1.3 核心组件概览)
- [2. std::future - 异步结果的接收者](#2. std::future - 异步结果的接收者)
- [2.1 基本概念](#2.1 基本概念)
- [2.2 主要方法](#2.2 主要方法)
- [2.3 代码示例](#2.3 代码示例)
- [3. std::promise - 异步结果的提供者](#3. std::promise - 异步结果的提供者)
- [3.1 基本概念](#3.1 基本概念)
- [3.2 使用方法](#3.2 使用方法)
- [3.3 代码示例](#3.3 代码示例)
- [4. std::packaged_task - 可调用对象的包装器](#4. std::packaged_task - 可调用对象的包装器)
- [4.1 基本概念](#4.1 基本概念)
- [4.2 使用方法](#4.2 使用方法)
- [4.3 代码示例](#4.3 代码示例)
- [5. std::async - 最简单的异步任务启动](#5. std::async - 最简单的异步任务启动)
- [5.1 基本概念](#5.1 基本概念)
- [5.2 启动策略](#5.2 启动策略)
- [5.3 代码示例](#5.3 代码示例)
- [6. 四者对比与选择](#6. 四者对比与选择)
- [7. 实际应用场景](#7. 实际应用场景)
- [8. 最佳实践](#8. 最佳实践)
- [9. 常见问题](#9. 常见问题)
1. 异步编程基础
1.1 什么是异步编程
异步编程是一种编程范式,允许程序在等待某个耗时操作完成时,继续执行其他任务,而不是阻塞等待。
同步 vs 异步:
cpp
// 同步方式(阻塞)
int result = long_running_computation(); // 等待完成
do_something_else(); // 必须等上面完成后才能执行
// 异步方式(非阻塞)
std::future<int> result = std::async(long_running_computation); // 立即返回
do_something_else(); // 可以立即执行
int value = result.get(); // 需要时再获取结果
1.2 为什么需要异步编程
优势:
- ✅ 提高响应性:主线程不会被长时间阻塞
- ✅ 充分利用多核:多个任务可以并发执行
- ✅ 简化代码:比手动管理线程更简单
- ✅ 异常安全:异常可以通过 future 传递
适用场景:
- 文件I/O操作
- 网络请求
- 复杂计算
- 数据库查询
- 任何耗时操作
1.3 核心组件概览
| 组件 | 角色 | 使用场景 |
|---|---|---|
| std::future | 结果接收者 | 获取异步操作的结果 |
| std::promise | 结果提供者 | 在一个线程中设置结果,另一个线程获取 |
| std::packaged_task | 任务包装器 | 将函数包装为异步任务 |
| std::async | 任务启动器 | 最简单的启动异步任务的方式 |
关系图:
std::async → 返回 → std::future
std::promise → 创建 → std::future
std::packaged_task → 获取 → std::future
2. std::future - 异步结果的接收者
2.1 基本概念
std::future 是一个模板类,代表一个异步操作的未来结果。你可以把它想象成一张"提货单":
- 现在可能还没有结果
- 将来某个时间点结果会准备好
- 可以随时查询结果是否就绪
- 可以等待并获取结果
核心特性:
- 只能移动,不能复制
get()只能调用一次- 自动等待异步任务完成
2.2 主要方法
| 方法 | 功能 | 说明 |
|---|---|---|
get() |
获取结果 | 阻塞直到结果就绪,只能调用一次 |
wait() |
等待完成 | 阻塞直到结果就绪,但不获取结果 |
wait_for(duration) |
超时等待 | 等待指定时间,返回状态 |
wait_until(time_point) |
等到某时间点 | 等到指定时间点,返回状态 |
valid() |
检查有效性 | 检查是否有共享状态 |
wait_for 返回值:
std::future_status::ready- 结果已就绪std::future_status::timeout- 超时std::future_status::deferred- 任务尚未启动(延迟执行)
2.3 代码示例
示例1:基本使用 - 这是最常用的方法
cpp
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
int compute_square(int x) {
std::cout << "计算 " << x << " 的平方..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
return x * x;
}
int main() {
std::cout << "启动异步任务" << std::endl;
// 启动异步任务,返回 future
std::future<int> result = std::async(std::launch::async, compute_square, 10);
std::cout << "主线程可以继续做其他事..." << std::endl;
// 可以做其他事情
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "做了一些其他工作" << std::endl;
// 获取结果(如果还没完成会等待)
std::cout << "等待结果..." << std::endl;
int value = result.get();
std::cout << "结果: " << value << std::endl;
return 0;
}
示例2:超时等待
cpp
#include <iostream>
#include <future>
#include <chrono>
int slow_computation() {
std::this_thread::sleep_for(std::chrono::seconds(3));
return 42;
}
int main() {
std::future<int> result = std::async(std::launch::async, slow_computation);
std::cout << "等待结果(最多2秒)..." << std::endl;
// 等待最多2秒
std::future_status status = result.wait_for(std::chrono::seconds(2));
if (status == std::future_status::ready) {
std::cout << "结果已就绪: " << result.get() << std::endl;
} else if (status == std::future_status::timeout) {
std::cout << "超时!任务还在运行中..." << std::endl;
// 继续等待
std::cout << "继续等待..." << std::endl;
int value = result.get(); // 会阻塞直到完成
std::cout << "最终结果: " << value << std::endl;
}
return 0;
}
示例3:检查有效性
cpp
#include <iostream>
#include <future>
int main() {
std::future<int> f1;
std::cout << "f1 有效吗? " << (f1.valid() ? "是" : "否") << std::endl;
std::future<int> f2 = std::async(std::launch::async, []{ return 42; });
std::cout << "f2 有效吗? " << (f2.valid() ? "是" : "否") << std::endl;
int result = f2.get();
std::cout << "结果: " << result << std::endl;
// get() 后 future 变为无效
std::cout << "get() 后 f2 有效吗? " << (f2.valid() ? "是" : "否") << std::endl;
return 0;
}
3. std::promise - 异步结果的提供者
3.1 基本概念
std::promise 是结果的提供者 ,它允许你在一个线程中设置值或异常,然后在另一个线程中通过 std::future 获取。
promise 和 future 的关系:
线程A: promise.set_value(42) → 共享状态 → 线程B: future.get() 返回 42
核心特性:
- 一次性使用:只能设置一次值
- 配对使用:promise 和 future 配对
- 异常传递:可以传递异常
3.2 使用方法
基本步骤:
- 创建
std::promise<T>对象 - 通过
get_future()获取关联的std::future - 将 promise 传递给工作线程(使用
std::ref()) - 工作线程调用
set_value()或set_exception() - 主线程通过 future 的
get()获取结果
3.3 代码示例
示例1:基本使用
cpp
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
void compute(std::promise<int>& prom, int x) {
std::cout << "工作线程:开始计算..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
int result = x * x;
std::cout << "工作线程:计算完成,设置结果" << std::endl;
prom.set_value(result); // 设置结果
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future(); // 获取 future
// 启动线程,注意使用 std::ref()
std::thread t(compute, std::ref(prom), 10);
std::cout << "主线程:等待结果..." << std::endl;
// 获取结果(会阻塞直到 set_value 被调用)
int result = fut.get();
std::cout << "主线程:得到结果 = " << result << std::endl;
t.join();
return 0;
}
示例2:传递异常
cpp
#include <iostream>
#include <future>
#include <thread>
#include <stdexcept>
void risky_operation(std::promise<int>& prom, int x) {
try {
if (x < 0) {
throw std::invalid_argument("参数不能为负数!");
}
std::this_thread::sleep_for(std::chrono::seconds(1));
prom.set_value(x * x);
} catch (...) {
// 捕获异常并通过 promise 传递
prom.set_exception(std::current_exception());
}
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
// 传递负数,会触发异常
std::thread t(risky_operation, std::ref(prom), -5);
try {
int result = fut.get(); // 这里会重新抛出异常
std::cout << "结果: " << result << std::endl;
} catch (const std::exception& e) {
std::cout << "捕获到异常: " << e.what() << std::endl;
}
t.join();
return 0;
}
示例3:多个线程等待同一个结果(使用 shared_future)
cpp
#include <iostream>
#include <future>
#include <thread>
#include <vector>
void wait_for_signal(std::shared_future<void> fut, int id) {
std::cout << "线程 " << id << " 等待信号..." << std::endl;
fut.get(); // 等待信号
std::cout << "线程 " << id << " 收到信号,开始工作!" << std::endl;
}
int main() {
std::promise<void> signal;
std::shared_future<void> ready = signal.get_future().share();
std::vector<std::thread> threads;
// 创建多个等待线程
for (int i = 0; i < 5; ++i) {
threads.emplace_back(wait_for_signal, ready, i);
}
std::cout << "主线程:准备中..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "主线程:发送信号!" << std::endl;
signal.set_value(); // 释放所有等待线程
for (auto& t : threads) {
t.join();
}
return 0;
}
4. std::packaged_task - 可调用对象的包装器
4.1 基本概念
std::packaged_task 是一个可调用对象的包装器,它将函数、lambda 或函数对象包装成一个异步任务,可以在任何时候执行。
与 promise 的区别:
promise:手动设置值packaged_task:自动从函数返回值设置
4.2 使用方法
基本步骤:
- 创建
std::packaged_task<返回类型(参数类型)>对象 - 通过
get_future()获取关联的 future - 像普通函数一样调用 task(可以在任何线程)
- 通过 future 获取结果
4.3 代码示例
示例1:基本使用
cpp
#include <iostream>
#include <future>
#include <thread>
int multiply(int a, int b) {
std::cout << "执行乘法: " << a << " * " << b << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
return a * b;
}
int main() {
// 创建 packaged_task,包装函数
std::packaged_task<int(int, int)> task(multiply);
// 获取 future
std::future<int> result = task.get_future();
// 在新线程中执行任务(注意:task 是 move-only)
std::thread t(std::move(task), 6, 7);//std::packaged_task 是只能移动(move-only)的类型,不能被复制
std::cout << "主线程:等待结果..." << std::endl;
// 获取结果
std::cout << "结果: " << result.get() << std::endl;
t.join();
return 0;
}
示例2:使用 Lambda 表达式
cpp
#include <iostream>
#include <future>
#include <thread>
int main() {
// 使用 lambda 创建 packaged_task
std::packaged_task<int(int, int)> task([](int a, int b) {
std::cout << "Lambda 计算: " << a << " + " << b << std::endl;
return a + b;
});
std::future<int> result = task.get_future();
// 在当前线程执行(不一定要在新线程)
task(10, 20);
std::cout << "结果: " << result.get() << std::endl;
return 0;
}
示例3:任务队列(实际应用场景)
cpp
#include <iostream>
#include <future>
#include <queue>
#include <thread>
#include <mutex>
#include <functional>
class TaskQueue {
private:
std::queue<std::packaged_task<int()>> tasks_;
std::mutex mtx_;
std::thread worker_;
bool stop_ = false;
void worker_thread() {
while (true) {
std::packaged_task<int()> task;
{
std::lock_guard<std::mutex> lock(mtx_);
if (stop_ && tasks_.empty()) {
break;
}
if (!tasks_.empty()) {
task = std::move(tasks_.front());
tasks_.pop();
}
}
if (task.valid()) {
task(); // 执行任务
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
}
public:
TaskQueue() : worker_(&TaskQueue::worker_thread, this) {}
~TaskQueue() {
{
std::lock_guard<std::mutex> lock(mtx_);
stop_ = true;
}
worker_.join();
}
// 提交任务,返回 future
std::future<int> submit(std::function<int()> func) {
std::packaged_task<int()> task(func);
std::future<int> result = task.get_future();
{
std::lock_guard<std::mutex> lock(mtx_);
tasks_.push(std::move(task));
}
return result;
}
};
int main() {
TaskQueue queue;
// 提交多个任务
auto f1 = queue.submit([]{
std::cout << "任务1执行" << std::endl;
return 100;
});
auto f2 = queue.submit([]{
std::cout << "任务2执行" << std::endl;
return 200;
});
auto f3 = queue.submit([]{
std::cout << "任务3执行" << std::endl;
return 300;
});
// 获取结果
std::cout << "任务1结果: " << f1.get() << std::endl;
std::cout << "任务2结果: " << f2.get() << std::endl;
std::cout << "任务3结果: " << f3.get() << std::endl;
return 0;
}
5. std::async - 最简单的异步任务启动
5.1 基本概念
std::async 是最简单、最常用的启动异步任务的方式。它会自动创建线程(或复用线程),并返回一个 std::future 对象。
优势:
- ✅ 简单:一行代码启动异步任务
- ✅ 自动管理:自动管理线程生命周期
- ✅ 灵活:可以选择同步或异步执行
- ✅ 异常安全:自动传递异常
5.2 启动策略
| 策略 | 说明 | 何时执行 |
|---|---|---|
std::launch::async |
异步执行 | 立即在新线程中执行 |
std::launch::deferred |
延迟执行 | 调用 get() 或 wait() 时在当前线程执行 |
| `std::launch::async | std::launch::deferred` | 自动选择(默认) |
5.3 代码示例
示例1:基本使用
cpp
#include <iostream>
#include <future>
#include <chrono>
int compute(int x) {
std::cout << "计算中..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
return x * x;
}
int main() {
std::cout << "启动异步任务" << std::endl;
// 最简单的方式:使用 async
std::future<int> result = std::async(std::launch::async, compute, 10);
std::cout << "做其他工作..." << std::endl;
// 获取结果
std::cout << "结果: " << result.get() << std::endl;
return 0;
}
示例2:启动策略对比
cpp
#include <iostream>
#include <future>
#include <thread>
void print_thread_id(const std::string& label) {
std::cout << label << " 线程ID: "
<< std::this_thread::get_id() << std::endl;
}
int task() {
print_thread_id("任务执行在");
return 42;
}
int main() {
print_thread_id("主线程");
// 异步执行(新线程)
std::cout << "\n--- std::launch::async ---" << std::endl;
auto f1 = std::async(std::launch::async, task);
f1.get();
// 延迟执行(当前线程)
std::cout << "\n--- std::launch::deferred ---" << std::endl;
auto f2 = std::async(std::launch::deferred, task);
std::cout << "还没有执行任务..." << std::endl;
f2.get(); // 这时才在当前线程执行
return 0;
}
示例3:并发执行多个任务
cpp
#include <iostream>
#include <future>
#include <vector>
#include <chrono>
#include <numeric>
// 计算数组部分和
int partial_sum(const std::vector<int>& data, size_t start, size_t end) {
std::cout << "计算 [" << start << ", " << end << ") 的和" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return std::accumulate(data.begin() + start, data.begin() + end, 0);
}
int main() {
// 创建大数组
std::vector<int> data(10000);
for (size_t i = 0; i < data.size(); ++i) {
data[i] = i + 1;
}
auto start_time = std::chrono::high_resolution_clock::now();
// 将工作分成4个任务并发执行
size_t chunk_size = data.size() / 4;
std::vector<std::future<int>> futures;
for (int i = 0; i < 4; ++i) {
size_t start = i * chunk_size;
size_t end = (i == 3) ? data.size() : (i + 1) * chunk_size;
futures.push_back(std::async(std::launch::async,
partial_sum,
std::ref(data), // 注意:使用 std::ref()
start,
end));
}
// 收集结果
int total = 0;
for (auto& f : futures) {
total += f.get();
}
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "总和: " << total << std::endl;
std::cout << "用时: " << duration.count() << " ms" << std::endl;
return 0;
}
示例4:异常处理
cpp
#include <iostream>
#include <future>
#include <stdexcept>
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("除数不能为0!");
}
return a / b;
}
int main() {
// 正常情况
try {
auto f1 = std::async(std::launch::async, divide, 10, 2);
std::cout << "10 / 2 = " << f1.get() << std::endl;
} catch (const std::exception& e) {
std::cout << "错误: " << e.what() << std::endl;
}
// 异常情况
try {
auto f2 = std::async(std::launch::async, divide, 10, 0);
std::cout << "10 / 0 = " << f2.get() << std::endl; // 这里会抛出异常
} catch (const std::exception& e) {
std::cout << "捕获异常: " << e.what() << std::endl;
}
return 0;
}
6. 四者对比与选择
6.1 特性对比
| 特性 | std::async | std::promise | std::packaged_task | std::future |
|---|---|---|---|---|
| 作用 | 启动异步任务 | 手动设置结果 | 包装可调用对象 | 获取结果 |
| 返回值 | 返回 future | 创建 future | 获取 future | - |
| 易用性 | ⭐⭐⭐⭐⭐ 最简单 | ⭐⭐⭐ 需要手动设置 | ⭐⭐⭐⭐ 较简单 | ⭐⭐⭐⭐⭐ 透明 |
| 灵活性 | ⭐⭐⭐ 中等 | ⭐⭐⭐⭐⭐ 最灵活 | ⭐⭐⭐⭐ 灵活 | ⭐⭐ 只读 |
| 线程管理 | 自动 | 手动 | 手动 | - |
| 典型用途 | 简单异步任务 | 复杂同步场景 | 任务队列、延迟执行 | 获取异步结果 |
6.2 使用场景建议
使用 std::async 的场景:
cpp
// ✅ 简单的异步计算
auto result = std::async(std::launch::async, compute_something, arg);
// ✅ 快速并发多个任务
std::vector<std::future<int>> results;
for (int i = 0; i < 10; ++i) {
results.push_back(std::async(std::launch::async, task, i));
}
适用于:
- 简单的一次性异步任务
- 快速原型开发
- 不需要精细控制线程的场景
使用 std::promise 的场景:
cpp
// ✅ 需要在任意时刻设置结果
void worker(std::promise<Result>& prom) {
// ... 复杂逻辑
if (condition) {
prom.set_value(result);
} else {
prom.set_exception(...);
}
}
// ✅ 线程间信号通知
std::promise<void> ready_signal;
// 多个线程等待信号...
ready_signal.set_value(); // 释放所有等待线程
适用于:
- 需要在任意位置设置结果
- 复杂的线程同步逻辑
- 一对多的信号通知
使用 std::packaged_task 的场景:
cpp
// ✅ 任务队列
std::queue<std::packaged_task<int()>> task_queue;
// ✅ 延迟执行
std::packaged_task<int()> task(compute);
auto future = task.get_future();
// ... 稍后再执行
task();
// ✅ 线程池
void thread_pool_worker() {
while (true) {
auto task = get_task_from_queue();
task(); // 执行任务
}
}
适用于:
- 任务队列系统
- 线程池实现
- 需要延迟执行的任务
7. 实际应用场景
场景1:并发下载文件
cpp
#include <iostream>
#include <future>
#include <vector>
#include <string>
#include <chrono>
// 模拟下载文件
std::string download_file(const std::string& url) {
std::cout << "开始下载: " << url << std::endl;
// 模拟网络延迟
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "下载完成: " << url << std::endl;
return "Content of " + url;
}
int main() {
std::vector<std::string> urls = {
"http://example.com/file1.txt",
"http://example.com/file2.txt",
"http://example.com/file3.txt",
"http://example.com/file4.txt"
};
auto start = std::chrono::high_resolution_clock::now();
// 并发下载所有文件
std::vector<std::future<std::string>> futures;
for (const auto& url : urls) {
futures.push_back(std::async(std::launch::async, download_file, url));
}
// 收集所有结果
std::vector<std::string> contents;
for (auto& f : futures) {
contents.push_back(f.get());
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
std::cout << "\n全部下载完成!" << std::endl;
std::cout << "总共用时: " << duration.count() << " 秒" << std::endl;
std::cout << "(如果顺序下载需要 " << urls.size() * 2 << " 秒)" << std::endl;
return 0;
}
场景2:数据库查询与UI更新
cpp
#include <iostream>
#include <future>
#include <thread>
#include <string>
// 模拟数据库查询(耗时操作)
std::string query_database(const std::string& sql) {
std::cout << "执行查询: " << sql << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
return "查询结果数据...";
}
// 模拟UI更新
void update_ui(const std::string& data) {
std::cout << "UI更新: " << data << std::endl;
}
int main() {
std::cout << "应用启动" << std::endl;
// 异步执行数据库查询,不阻塞主线程
auto result = std::async(std::launch::async,
query_database,
"SELECT * FROM users");
// 主线程可以继续响应用户操作
std::cout << "主线程保持响应,可以处理用户输入..." << std::endl;
for (int i = 1; i <= 3; ++i) {
std::cout << " 处理用户事件 " << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// 查询完成后更新UI
std::string data = result.get();
update_ui(data);
return 0;
}
场景3:生产者-消费者(使用 promise)
cpp
#include <iostream>
#include <future>
#include <thread>
#include <queue>
#include <mutex>
std::mutex mtx;
std::queue<int> data_queue;
void producer(std::promise<void>& done_signal) {
for (int i = 0; i < 10; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
data_queue.push(i);
std::cout << "生产: " << i << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
// 生产完成,发送信号
done_signal.set_value();
}
void consumer(std::future<void>& done_signal) {
while (done_signal.wait_for(std::chrono::milliseconds(0)) != std::future_status::ready) {
std::lock_guard<std::mutex> lock(mtx);
if (!data_queue.empty()) {
int value = data_queue.front();
data_queue.pop();
std::cout << " 消费: " << value << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(150));
}
// 处理剩余数据
std::lock_guard<std::mutex> lock(mtx);
while (!data_queue.empty()) {
int value = data_queue.front();
data_queue.pop();
std::cout << " 消费(剩余): " << value << std::endl;
}
}
int main() {
std::promise<void> done_signal;
std::future<void> done_future = done_signal.get_future();
std::thread prod(producer, std::ref(done_signal));
std::thread cons(consumer, std::ref(done_future));
prod.join();
cons.join();
std::cout << "全部完成" << std::endl;
return 0;
}
8. 最佳实践
8.1 总是检查 future 的有效性
cpp
// ✅ 好的做法
std::future<int> result = std::async(task);
if (result.valid()) {
int value = result.get();
}
// ❌ 不好的做法
std::future<int> result;
int value = result.get(); // 未定义行为!
8.2 只调用 get() 一次
cpp
// ❌ 错误:get() 只能调用一次
std::future<int> result = std::async(task);
int v1 = result.get();
int v2 = result.get(); // 错误!future 已经无效
// ✅ 正确:保存结果
std::future<int> result = std::async(task);
int value = result.get();
// 使用 value...
8.3 使用 shared_future 实现多次获取
cpp
// ✅ 需要多次获取结果时使用 shared_future
std::future<int> fut = std::async(task);
std::shared_future<int> shared_fut = fut.share();
int v1 = shared_fut.get(); // OK
int v2 = shared_fut.get(); // OK,可以多次调用
8.4 注意 future 的析构行为
cpp
// ⚠️ 注意:async 返回的 future 析构时会阻塞
{
std::async(std::launch::async, long_task);
} // 这里会阻塞等待 long_task 完成!
// ✅ 如果想要"发射后不管",保存 future
auto result = std::async(std::launch::async, long_task);
// ... 做其他事情
// 需要时再 get()
8.5 传递引用时使用 std::ref()
cpp
void process(Data& data) {
// ...
}
Data data;
// ❌ 错误:会尝试复制 data
// std::async(process, data);
// ✅ 正确:使用 std::ref()
std::async(std::launch::async, process, std::ref(data));
8.6 异常处理
cpp
// ✅ 异常会通过 future 传递
try {
auto result = std::async([]{
throw std::runtime_error("出错了!");
return 42;
});
int value = result.get(); // 在这里重新抛出异常
} catch (const std::exception& e) {
std::cout << "捕获异常: " << e.what() << std::endl;
}
8.7 选择合适的启动策略
cpp
// 🔧 根据需求选择策略
// 明确需要并发执行
auto f1 = std::async(std::launch::async, task);
// 可以延迟到需要时执行(节省资源)
auto f2 = std::async(std::launch::deferred, task);
// 让系统决定(默认)
auto f3 = std::async(task);
9. 常见问题
Q1: std::async、std::thread 该用哪个?
答:
| 场景 | 推荐 | 原因 |
|---|---|---|
| 需要返回值 | std::async |
自动通过 future 返回结果 |
| 不需要返回值 | std::thread |
更直接,开销稍小 |
| 简单任务 | std::async |
代码更简洁 |
| 需要精确控制线程 | std::thread |
更灵活 |
| 异常处理 | std::async |
异常自动传递 |
Q2: promise 只能设置一次值吗?
答: 是的。如果尝试多次设置,会抛出 std::future_error 异常:
cpp
std::promise<int> prom;
prom.set_value(42); // OK
prom.set_value(100); // 抛出异常!
Q3: 为什么 async 有时不创建新线程?
答: 使用默认策略或 std::launch::deferred 时,任务可能延迟执行:
cpp
auto f = std::async(task); // 默认策略,可能延迟
// 解决方案:明确指定 async
auto f = std::async(std::launch::async, task); // 强制异步
Q4: future.get() 超时了怎么办?
答: get() 没有超时版本,但可以用 wait_for():
cpp
std::future<int> result = std::async(task);
if (result.wait_for(std::chrono::seconds(5)) == std::future_status::ready) {
int value = result.get(); // 不会阻塞
} else {
std::cout << "超时!" << std::endl;
// 任务仍在运行...
}
Q5: packaged_task 和 promise 有什么区别?
答:
cpp
// promise:手动设置
std::promise<int> prom;
prom.set_value(42);
// packaged_task:自动从函数返回值设置
std::packaged_task<int()> task([]{ return 42; });
task(); // 自动设置结果
// 选择:
// - 需要灵活控制时间和条件 → promise
// - 包装函数/lambda → packaged_task
Q6: 如何取消一个异步任务?
答: C++ 标准库不直接支持取消,需要自己实现:
cpp
#include <atomic>
std::atomic<bool> should_stop{false};
void cancellable_task() {
for (int i = 0; i < 1000; ++i) {
if (should_stop.load()) {
std::cout << "任务被取消" << std::endl;
return;
}
// 做一些工作...
}
}
int main() {
auto result = std::async(std::launch::async, cancellable_task);
std::this_thread::sleep_for(std::chrono::seconds(1));
should_stop.store(true); // 请求取消
result.wait();
return 0;
}
编译和运行
编译示例代码
bash
# 基本编译(需要 C++11 支持)
g++ -std=c++11 -pthread example.cpp -o example
# 启用优化
g++ -std=c++11 -pthread -O2 example.cpp -o example
# C++17(支持更多特性)
g++ -std=c++17 -pthread example.cpp -o example
运行示例
bash
./example
参考资料
官方文档
- C++ Reference - std::future
- C++ Reference - std::promise
- C++ Reference - std::packaged_task
- C++ Reference - std::async
推荐书籍
- C++ Concurrency in Action (2nd Edition) - Anthony Williams
- Effective Modern C++ - Scott Meyers
总结
核心要点
-
std::future - 异步结果的接收者
- 像"提货单",代表未来的结果
get()获取结果(只能调用一次)wait_for()实现超时等待
-
std::promise - 异步结果的提供者
- 手动设置结果或异常
- 适合复杂的线程同步场景
- 与 future 配对使用
-
std::packaged_task - 可调用对象的包装器
- 将函数包装成异步任务
- 自动从返回值设置结果
- 适合任务队列和线程池
-
std::async - 最简单的异步启动方式
- 一行代码启动异步任务
- 自动管理线程
- 首选方案(除非需要精细控制)
选择指南
需要异步执行任务?
├─ 简单任务,需要返回值
│ └─> 使用 std::async ⭐ 推荐
│
├─ 需要在任意时刻设置结果
│ └─> 使用 std::promise
│
├─ 需要包装函数,延迟执行
│ └─> 使用 std::packaged_task
│
└─ 只需要获取结果
└─> 使用 std::future
最佳实践回顾
- ✅ 优先使用
std::async(最简单) - ✅ 检查
future.valid()再调用get() - ✅
get()只能调用一次 - ✅ 传递引用时使用
std::ref() - ✅ 使用
wait_for()实现超时 - ✅ 异常会自动通过 future 传递
- ✅ 需要多次获取结果用
shared_future
希望这份指南能帮助您掌握C++异步编程,编写高效、响应迅速的多线程程序!