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` 可以安全地获取结果或异常。