跳转到内容

C++ packaged tas

来自代码酷


C++ packaged_task 是 C++11 标准引入的一个高级多线程工具,它允许将可调用对象(如函数、Lambda表达式或函数对象)与一个future对象关联起来,从而在异步任务完成后获取结果。`packaged_task` 提供了一种便捷的方式将任务的执行与结果的获取分离,常用于线程池、任务队列等场景。

概述[编辑 | 编辑源代码]

`std::packaged_task` 是一个类模板,其模板参数为函数签名(例如 `int(int, int)`)。它封装了一个可调用对象,并允许通过关联的 `std::future` 对象获取该任务的返回值。其主要特点包括:

  • 将任务(函数)的执行与结果的存储绑定。
  • 通过 `get_future()` 方法获取一个 `future` 对象,用于异步获取结果。
  • 任务可以通过调用 `operator()` 直接执行,或传递给线程执行。

基本用法[编辑 | 编辑源代码]

以下是一个简单的示例,展示 `packaged_task` 的基本用法:

#include <iostream>
#include <future>
#include <thread>

int add(int a, int b) {
    return a + b;
}

int main() {
    // 创建一个 packaged_task,封装 add 函数
    std::packaged_task<int(int, int)> task(add);

    // 获取与任务关联的 future
    std::future<int> result = task.get_future();

    // 将任务移动到线程中执行
    std::thread t(std::move(task), 2, 3);

    // 等待结果并输出
    std::cout << "Result: " << result.get() << std::endl;

    t.join();
    return 0;
}

输出:

Result: 5

代码解释: 1. 创建一个 `packaged_task` 对象 `task`,封装了 `add` 函数。 2. 调用 `task.get_future()` 获取关联的 `future` 对象 `result`。 3. 将 `task` 移动到线程 `t` 中执行,并传递参数 `2` 和 `3`。 4. 主线程通过 `result.get()` 阻塞等待结果并输出。

与 Lambda 表达式结合[编辑 | 编辑源代码]

`packaged_task` 可以与 Lambda 表达式结合,实现更灵活的任务定义:

#include <iostream>
#include <future>
#include <thread>

int main() {
    // 创建一个 packaged_task,封装 Lambda 表达式
    std::packaged_task<int()> task([]() {
        return 42;
    });

    // 获取 future
    std::future<int> result = task.get_future();

    // 直接执行任务(也可以传递给线程)
    task();

    // 输出结果
    std::cout << "The answer is: " << result.get() << std::endl;

    return 0;
}

输出:

The answer is: 42

实际应用场景[编辑 | 编辑源代码]

`packaged_task` 常用于以下场景: 1. 线程池:将任务封装为 `packaged_task` 并提交到线程池,通过 `future` 获取结果。 2. 异步任务链:将多个任务串联,前一个任务的输出作为后一个任务的输入。 3. 延迟计算:在需要时才启动任务执行。

线程池示例[编辑 | 编辑源代码]

以下是一个简化的线程池实现片段,展示 `packaged_task` 的典型用法:

#include <iostream>
#include <future>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

class ThreadPool {
public:
    ThreadPool(size_t num_threads) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::packaged_task<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(queue_mutex);
                        condition.wait(lock, [this] { return !tasks.empty() || stop; });
                        if (stop && tasks.empty()) return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    template <typename F>
    auto enqueue(F&& f) -> std::future<decltype(f())> {
        using return_type = decltype(f());
        auto task = std::packaged_task<return_type()>(std::forward<F>(f));
        auto res = task.get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            tasks.emplace(std::move(task));
        }
        condition.notify_one();
        return res;
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (auto& worker : workers) {
            worker.join();
        }
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::packaged_task<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop = false;
};

int main() {
    ThreadPool pool(4);
    auto result = pool.enqueue([]() {
        return "Hello from thread pool!";
    });
    std::cout << result.get() << std::endl;
    return 0;
}

输出:

Hello from thread pool!

与 std::async 的比较[编辑 | 编辑源代码]

`packaged_task` 和 `std::async` 都可用于异步执行任务,但有以下区别:

特性 `packaged_task` `std::async`
完全控制任务的启动方式 | 由系统决定是否启动新线程
更高,可手动管理线程 | 较低,但更简单
需要精细控制任务执行时 | 快速实现异步调用时

状态与异常处理[编辑 | 编辑源代码]

  • 如果任务抛出异常,异常会被存储在 `future` 中,并在调用 `get()` 时重新抛出。
  • `packaged_task` 只能被移动(move),不能被复制。

异常处理示例[编辑 | 编辑源代码]

#include <iostream>
#include <future>
#include <thread>

void might_throw(bool do_throw) {
    if (do_throw) {
        throw std::runtime_error("Exception from task!");
    }
    std::cout << "Task completed normally." << std::endl;
}

int main() {
    std::packaged_task<void(bool)> task(might_throw);
    auto future = task.get_future();

    std::thread t(std::move(task), true);
    t.join();

    try {
        future.get();
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }

    return 0;
}

输出:

Caught exception: Exception from task!

总结[编辑 | 编辑源代码]

  • `packaged_task` 是一个强大的工具,用于将任务与结果分离。
  • 它提供了对任务执行的精细控制,适用于需要手动管理线程的场景。
  • 通过 `future` 可以安全地获取结果或异常。

模板:C++多线程导航