<future>
是 C++11 引入的重要头文件,提供了一套机制用于异步操作、线程间通信以及任务同步。通过 <future>
,你可以启动异步任务、获取其返回结果、处理任务异常,并使用 std::promise
来手动设置异步操作的结果。该库主要包含以下几个核心组件:
- std::future:用于获取异步操作的结果。
- std::async:启动异步任务并返回一个
std::future
对象。 - std::promise:与
std::future
搭配使用,用于在某个线程中设置结果或异常。 - std::packaged_task:包装可调用对象,将其执行结果与
std::future
绑定。
目录
<future>
头文件简介- std::future 与异步操作
- 2.1 使用 std::async 启动异步任务
- 2.2 等待与获取结果
- std::promise 与 std::packaged_task
- std::shared_future
- 异常处理
- 常见注意事项
- 结论
1. <future>
头文件简介
C++ <future>
提供了支持异步操作的机制。它允许你启动并行任务,并在任务完成后获取其结果,而不必阻塞主线程。此外,<future>
支持异常传递,使得在异步任务中抛出的异常可以在等待任务结果时被捕获。
使用这些功能时,只需包含头文件:
#include <future>
2. std::future 与异步操作
2.1 使用 std::async 启动异步任务
std::async
用于启动一个异步任务,它会立即返回一个 std::future
对象,该对象将来可以提供任务的返回值。std::async
可以采用两种策略:
- 延迟调用(deferred):任务直到调用
future::get()
时才执行。 - 异步调用(async):任务立即在新的线程中执行。
示例:
#include <iostream>
#include <future>
#include <chrono>
int computeSquare(int x) {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
return x * x;
}
int main() {
// 启动异步任务,采用异步调用策略
std::future<int> fut = std::async(std::launch::async, computeSquare, 5);
std::cout << "Doing other work..." << std::endl;
// 获取异步任务的结果,若任务未完成则阻塞等待
int result = fut.get();
std::cout << "Result: " << result << std::endl; // 输出:25
return 0;
}
2.2 等待与获取结果
- future::get():阻塞调用直到异步任务完成,并返回任务结果。如果任务中抛出了异常,该异常会在调用
get()
时重新抛出。 - future::wait():等待异步任务完成,但不获取结果。
- future::wait_for() / wait_until():提供带超时等待的接口。
3. std::promise 与 std::packaged_task
3.1 std::promise 的基本使用
std::promise
提供了一种在不同线程之间传递值或异常的机制。你可以在一个线程中设置值,然后在另一个线程中通过与之关联的 std::future
获取该值。
示例:
#include <iostream>
#include <future>
#include <thread>
void promiseTask(std::promise<int>& prom) {
try {
// 进行一些计算或操作
int result = 42;
prom.set_value(result); // 设置结果
} catch(...) {
prom.set_exception(std::current_exception()); // 捕获异常并传递
}
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t(promiseTask, std::ref(prom));
t.join();
try {
int value = fut.get();
std::cout << "Promise result: " << value << std::endl;
} catch (const std::exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
return 0;
}
3.2 std::packaged_task 的使用
std::packaged_task
用于将一个可调用对象(如函数或 lambda)包装起来,并将其执行结果与一个 std::future
对象绑定。这样,你可以在稍后调用任务,并获取其结果。
示例:
#include <iostream>
#include <future>
#include <thread>
int multiply(int a, int b) {
return a * b;
}
int main() {
// 包装任务
std::packaged_task<int(int, int)> task(multiply);
std::future<int> result = task.get_future();
// 在单独的线程中执行任务
std::thread t(std::move(task), 6, 7);
t.join();
std::cout << "Multiplication result: " << result.get() << std::endl; // 输出:42
return 0;
}
4. std::shared_future
有时你可能希望多个线程共享同一个异步任务的结果。这时可以使用 std::shared_future
,它允许多个消费者通过拷贝共享同一个 future 对象,并多次调用 get()
。
示例:
#include <iostream>
#include <future>
#include <thread>
int computeValue() {
return 100;
}
int main() {
std::future<int> fut = std::async(std::launch::async, computeValue);
std::shared_future<int> sharedFut = fut.share();
// 多个线程共享相同的结果
std::thread t1([sharedFut]() {
std::cout << "Thread 1 got: " << sharedFut.get() << std::endl;
});
std::thread t2([sharedFut]() {
std::cout << "Thread 2 got: " << sharedFut.get() << std::endl;
});
t1.join();
t2.join();
return 0;
}
5. 异常处理
在异步任务中,如果出现异常,可通过 std::future::get()
在获取结果时重新抛出异常。使用 std::promise::set_exception
可将异常传递到 future 对象。
示例:
#include <iostream>
#include <future>
#include <thread>
#include <stdexcept>
int taskThatMayThrow(bool trigger) {
if(trigger)
throw std::runtime_error("Error occurred in async task!");
return 10;
}
int main() {
std::future<int> fut = std::async(std::launch::async, taskThatMayThrow, true);
try {
int value = fut.get();
std::cout << "Task result: " << value << std::endl;
} catch (const std::exception& ex) {
std::cout << "Caught exception: " << ex.what() << std::endl;
}
return 0;
}
6. 常见注意事项
- 确保调用 get(): 对于每个
std::future
对象,确保调用一次get()
或使用等待函数,否则可能导致程序挂起或资源泄漏。 - 任务策略: 选择合适的调用策略(如
std::launch::async
或std::launch::deferred
)以匹配任务需求。 - 异常传递: 在异步任务中正确捕获并传递异常,确保调用者能够检测并处理异常情况。
7. 结论
C++ <future>
提供了灵活且强大的机制用于异步任务、线程间通信与任务同步。通过 std::async
、std::promise
、std::packaged_task
以及 std::shared_future
等组件,你可以编写更具响应性和并发性的程序,同时确保异步操作中的异常也能被正确处理。掌握这些工具是现代 C++ 并发编程的重要技能之一。
推荐阅读:
发表回复