std::promise
和 std::packaged_task
都是 C++11 标准库中用于管理异步操作的工具,它们都允许你通过 std::future
获取异步操作的结果。然而,它们在设计目的和使用场景上有显著的区别。以下是对两者的详细比较:
std::promise
主要用途
- 手动设置结果:
std::promise
提供了一种机制来手动设置异步操作的结果或异常,并通过关联的std::future
对象获取该结果。 - 灵活控制:适用于需要显式控制何时以及如何设置结果的场景。
常用方法
- set_value():设置共享状态的结果(适用于返回值类型)。
void set_value(const T& value); // 或者 void set_value(T&& value);
set_exception():设置共享状态的异常信息。
void set_exception(std::exception_ptr p);
get_future():返回一个与当前 std::promise
关联的 std::future
对象。
std::future<T> get_future();
#include <iostream> #include <future> #include <thread> void producer(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(); // 获取与 promise 关联的 future std::thread t(producer, std::move(prom)); // 启动新线程执行生产者任务 try { int value = fut.get(); // 阻塞直到共享状态就绪 std::cout << "Promise returned: " << value << "\n"; } catch (const std::exception& e) { std::cerr << "Exception caught: " << e.what() << "\n"; } t.join(); // 等待线程结束 return 0; }
std::packaged_task
主要用途
- 包装可调用对象:
std::packaged_task
将任何可调用对象(如函数、lambda 表达式或函数对象)包装成一个异步任务,并返回一个std::future
对象来获取该任务的结果。 - 自动设置结果:任务执行完成后,
std::packaged_task
自动设置结果或异常到关联的std::future
对象。
常用方法
构造函数:接受一个可调用对象并包装它。
template< class Function > explicit packaged_task( Function&& f );
operator():调用被包装的任务。
void operator()( Args... args );
get_future():返回一个与当前 std::packaged_task
关联的 std::future
对象。
get_future():返回一个与当前 std::packaged_task 关联的 std::future 对象。
#include <iostream> #include <future> #include <thread> int task_function(int x) { return x * x; } int main() { std::packaged_task<int(int)> task(task_function); // 包装任务 std::future<int> fut = task.get_future(); // 获取与 packaged_task 关联的 future std::thread t(std::move(task), 5); // 启动新线程执行任务 int value = fut.get(); // 阻塞直到任务完成 std::cout << "Packaged task returned: " << value << "\n"; t.join(); // 等待线程结束 return 0; }
特性 | std::promise | std::packaged_task |
---|---|---|
主要用途 | 手动设置异步操作的结果或异常 | 包装可调用对象为异步任务,并自动设置结果或异常 |
灵活性 | 更灵活,适合需要显式控制何时及如何设置结果的场景 | 较少灵活性,但更方便直接执行可调用对象 |
设置结果的方式 | 使用 set_value() 和 set_exception() |
自动设置结果或异常 |
获取 std::future |
通过 get_future() |
通过 get_future() |
适用场景 | 当你需要在多个地方设置结果或处理复杂的异步逻辑时 | 当你需要简单地将一个函数或可调用对象包装为异步任务时 |
具体应用场景
-
std::promise
:- 当你需要在多个不同的地方设置异步操作的结果或异常时。
- 当你需要手动控制何时以及如何设置结果时。
- 在复杂的多线程环境中,可能需要在不同线程之间传递数据和状态时。
-
std::packaged_task
:- 当你有一个具体的可调用对象(如函数或 lambda 表达式),并且希望将其作为异步任务执行时。
- 当你需要简化异步任务的创建和管理时,特别是当任务的结果可以直接从可调用对象中获得时。
综合示例
为了更好地理解两者的区别,下面是一个综合示例,展示了如何分别使用
std::promise
和std::packaged_task
来实现相同的功能:#include <iostream> #include <future> #include <thread> void async_operation(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(async_operation, std::move(prom)); int value = fut.get(); std::cout << "Promise returned: " << value << "\n"; t.join(); return 0; }
使用
std::packaged_task
#include <iostream> #include <future> #include <thread> int async_operation(int x) { return x * x; // 模拟异步操作 } int main() { std::packaged_task<int(int)> task(async_operation); std::future<int> fut = task.get_future(); std::thread t(std::move(task), 5); int value = fut.get(); std::cout << "Packaged task returned: " << value << "\n"; t.join(); return 0; }
总结
std::promise
提供了更高的灵活性,适合需要手动控制何时及如何设置异步操作结果的场景。std::packaged_task
更加便捷,适合将一个具体的可调用对象包装为异步任务,并自动管理其结果或异常。
根据具体的需求选择合适的工具可以提高代码的可读性和维护性。