🍑个人主页:Jupiter.
🚀 所属专栏:C++学习笔记
欢迎大家点赞收藏评论😊

在这里插入图片描述

在这里插入图片描述


本文介绍了C++中三个重要的异步编程工具:

  • std::mem_fn:将成员函数转换为可调用对象,适用于需要将成员函数作为回调的场景,与std::bind相比需要显式传递对象实例。

  • std::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 核心函数
  1. 构造函数
	promise():默认构造函数,创建一个空的 promise 对象。
	promise(promise&& other):移动构造函数,转移所有权。
  1. 设置值
	void set_value(const T& value):设置异步任务的结果值。
	void set_value(T&& value)(移动版本):避免拷贝,提高性能。
	void set_exception(std::exception_ptr e):设置异常,供 future.get() 重新抛出。
  1. 获取 std::future
	std::future<T> get_future():返回关联的 std::future 对象,用于获取结果。
std::future 核心函数
  1. 获取结果
	T get():阻塞当前线程,直到结果可用,返回结果值。
	//如果异步任务抛出异常,get() 会重新抛出该异常。
	//只能调用一次,调用后 future 变为无效。
  1. 状态检查
	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 包装可调用对象 线程池任务提交

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐