初识 C++11 异步编程核心组件:mem_fn、promise/future 与 packaged_task 的高效用法
本文介绍了C++中三个重要的异步编程工具: std::mem_fn:将成员函数转换为可调用对象,适用于需要将成员函数作为回调的场景,与std::bind相比需要显式传递对象实例。 std::promise和std::future:用于异步任务间的结果传递,promise用于设置值或异常,future用于获取结果,两者必须一一对应,且future.get()只能调用一次。 std::packaged
·

目录
本文介绍了C++中三个重要的异步编程工具:
-
std::mem_fn:
将成员函数转换为可调用对象,适用于需要将成员函数作为回调的场景,与std::bind相比需要显式传递对象实例。 -
s
td::promise和std::future:
用于异步任务间的结果传递,promise用于设置值或异常,future用于获取结果,两者必须一一对应,且future.get()只能调用一次。 -
std::packaged_task:
包装可调用对象并关联future,适用于线程池任务提交,与std::function不同之处在于它专为异步任务设计,支持返回值获取。
这些工具为C++异步编程提供了强大支持,能够简化线程间通信和任务管理。
std::mem_fn:成员函数绑定
作用
- 将类的成员函数转换为可调用对象,使其可以像普通函数一样被调用。
- 适用于需要将成员函数作为回调或提交到线程池的场景。
原理
- std::mem_fn 生成一个函数对象,该对象接受一个指向类实例的指针(或引用)作为隐含参数,并调用对应的成员函数。
示例:
#include <functional>
#include <iostream>
class Dog {
public:
void bark() const {
std::cout << "Woof!" << std::endl;
}
};
int main() {
Dog dog;
auto bark_func = std::mem_fn(&Dog::bark);
bark_func(dog); // 输出 "Woof!"
return 0;
}
与 std::bind 的区别
- std::bind 可以绑定成员函数和对象实例,生成一个无参可调用对象:
auto bound_func = std::bind(&Dog::bark, &dog);
bound_func(); // 输出 "Woof!"
- std::mem_fn 生成的函数对象需要显式传递 this 指针:
auto mem_func = std::mem_fn(&Dog::bark);
mem_func(dog); // 必须传递 dog 对象
std::promise 和 std::future:异步结果传递
作用
- std::promise:用于在异步任务中设置一个值或异常,供 std::future 获取。
- std::future:用于获取异步操作的结果(通过 get() 方法),阻塞调用直到结果可用。
原理
- std::promise 和 std::future 是一对配套使用的对象,通过 std::promise::get_future() 关联。
工作流程:
- 创建 std::promise 对象。
- 调用 get_future() 获取关联的 std::future 对象。
- 在异步任务中,通过 promise.set_value() 设置结果。
- 在主线程中,通过 future.get() 获取结果(阻塞等待)。
#include <future>
#include <iostream>
#include <thread>
void async_task(std::promise<int> promise) {
// 模拟耗时操作
std::this_thread::sleep_for(std::chrono::seconds(1));
promise.set_value(42); // 设置结果
}
int main() {
std::promise<int> promise;
std::future<int> future = promise.get_future();
std::thread t(async_task, std::move(promise));
t.detach(); // 分离线程(或使用 t.join() 等待)
std::cout << "Waiting for result..." << std::endl;
int result = future.get(); // 阻塞等待结果
std::cout << "Result: " << result << std::endl; // 输出 42
return 0;
}
关键点
- std::promise 和 std::future 必须一一对应。
- future.get() 只能调用一次,调用后 future 变为无效。
- 如果异步任务抛出异常,可以通过 promise.set_exception() 传递异常,future.get() 会重新抛出该异常。
std::promise 核心函数
- 构造函数
promise():默认构造函数,创建一个空的 promise 对象。
promise(promise&& other):移动构造函数,转移所有权。
- 设置值
void set_value(const T& value):设置异步任务的结果值。
void set_value(T&& value)(移动版本):避免拷贝,提高性能。
void set_exception(std::exception_ptr e):设置异常,供 future.get() 重新抛出。
- 获取 std::future
std::future<T> get_future():返回关联的 std::future 对象,用于获取结果。
std::future 核心函数
- 获取结果
T get():阻塞当前线程,直到结果可用,返回结果值。
//如果异步任务抛出异常,get() 会重新抛出该异常。
//只能调用一次,调用后 future 变为无效。
- 状态检查
bool valid(); //检查 future 是否有效(是否关联了 promise 或任务)。
void wait(); //阻塞当前线程,直到结果可用。
template< class Rep, class Period >
std::future_status wait_for( const std::chrono::duration<Rep,Period>& timeout_duration );
//等待指定时间或直到结果可用。
//返回 std::future_status:
//ready:结果已就绪。
//timeout:超时未就绪。
//deferred:任务被延迟执行(如 std::async 的 std::launch::deferred)。
std::packaged_task:包装可调用对象
作用
- 包装一个可调用对象(如函数、lambda、成员函数等),并关联一个 std::future,用于获取其返回值。
- 适用于需要将任务提交到线程池或异步执行的场景。
原理
- std::packaged_task 是一个模板类,接受一个可调用对象的签名(如 int())。
- 调用 get_future() 获取关联的 std::future。
- 调用 operator() 执行任务,并将结果存储在 std::future 中。
#include <future>
#include <iostream>
#include <thread>
int compute() {
return 42;
}
int main() {
// 包装一个函数
std::packaged_task<int()> task(compute);
std::future<int> future = task.get_future();
// 在线程中执行任务
std::thread t(std::move(task));
t.detach();
// 获取结果
int result = future.get();
std::cout << "Result: " << result << std::endl; // 输出 42
return 0;
}
与线程池结合使用
class ThreadPool {
// ... 线程池实现 ...
public:
template <typename F>
auto commit(F&& f) -> std::future<decltype(f())> {
using RetType = decltype(f());
std::packaged_task<RetType()> task(std::forward<F>(f));
std::future<RetType> future = task.get_future();
// 提交 task 到任务队列(线程安全)
// ...
return future;
}
};
std::packaged_task 与 std::function 的区别
- std::packaged_task 和 std::function 都是 C++ 中用于封装可调用对象的工具,但它们的设计目标、使用场景和功能有显著差异。以下是详细对比:
设计目标
std::packaged_task
专为异步任务设计:
- 封装一个可调用对象,并关联一个 std::future,用于获取异步任务的结果。
- 适用于需要返回值的异步操作(如线程池任务)。
核心特性:
- 只能移动(不可复制),确保任务只能被执行一次。
- 提供 get_future() 方法,返回关联的 std::future 对象。
- 调用 operator() 时执行任务,并存储返回值到 std::future。
std::function
- 通用可调用对象包装器:
- 可以存储函数、lambda、函数对象、成员函数指针等。
- 适用于回调机制、延迟执行或统一函数签名。
核心特性:
- 可复制、可移动。
- 不直接支持异步结果获取(无 get_future() 方法)。
- 调用 operator() 时直接执行任务,不存储返回值(除非手动处理)。
关键区别
特性 | std::packaged_task | std::function |
---|---|---|
设计目标 | 异步任务结果获取 | 通用可调用对象存储 |
复制/移动 | 仅可移动(不可复制) | 可复制、可移动 |
返回值处理 | 通过 std::future 获取 | 需手动处理返回值 |
异常传递 | 支持通过 std::future 传递异常 | 需手动捕获并传递异常 |
典型场景 | 线程池任务、异步计算 | 回调函数、事件处理 |
代码示例对比
std::packaged_task 示例
#include <future>
#include <iostream>
#include <thread>
int compute() {
return 42;
}
int main() {
// 封装任务并获取 future
std::packaged_task<int()> task(compute);
std::future<int> future = task.get_future();
// 在线程中执行任务
std::thread t(std::move(task));
t.join();
// 通过 future 获取结果
std::cout << "Result: " << future.get() << std::endl; // 输出 42
return 0;
}
关键点:
- task.get_future() 返回 std::future,用于获取结果。
- 任务执行后,结果自动存储到 future 中。
std::function 示例
#include <functional>
#include <iostream>
#include <thread>
int compute() {
return 42;
}
int main() {
// 封装函数到 std::function
std::function<int()> func = compute;
// 直接调用(无异步支持)
int result = func();
std::cout << "Result: " << result << std::endl; // 输出 42
// 如果需要异步,需手动结合 std::thread 和 std::promise
// 示例:
std::promise<int> promise;
std::future<int> future = promise.get_future();
std::thread t([&promise, func]() {
try {
promise.set_value(func());
} catch (...) {
promise.set_exception(std::current_exception());
}
});
t.join();
std::cout << "Async result: " << future.get() << std::endl;
return 0;
}
关键点:
- std::function 本身不支持异步,需手动结合 std::thread 和 std::promise。
- 代码更复杂,需显式管理结果传递。
总结
特性 | std::packaged_task | std::function |
---|---|---|
异步支持 | 原生支持(通过 std::future) | 需手动实现 |
返回值处理 | 自动存储到 std::future | 需手动处理 |
异常传递 | 通过 std::future 自动传递 | 需手动捕获并传递 |
代码复杂度 | 简单(直接返回 future) | 复杂(需结合其他组件) |
典型用途 | 线程池、异步计算 | 回调、统一接口 |
异步编程模型总结
- 核心组件
组件 | 作用 | 典型使用场景 |
---|---|---|
std::mem_fn | 绑定成员函数 | 提交成员函数到线程池 |
std::promise | 设置异步结果 | 手动管理异步任务结果 |
std::future | 获取异步结果 | 阻塞等待任务完成 |
std::packaged_task | 包装可调用对象 | 线程池任务提交 |
更多推荐
所有评论(0)