这里我将为你详细讲解“C++11中std::packaged_task的使用详解”的完整攻略。
1. 什么是std::packaged_task
std::packaged_task
是一个封装了一个可调用对象(函数,函数指针,lambda表达式等)的类模板,其可以异步地执行该可调用对象,并在需要时获取该对象的结果。
std::packaged_task
可以通过 std::future
来获取调用结果,可以使用 future.get()
来获取结果,或者等待 future
函数执行完成再获取结果。
2. std::packaged_task的使用方法
2.1 使用示例1:使用 std::packaged_task 异步执行函数并获取结果
#include <iostream>
#include <thread>
#include <future>
#include <functional>
int foo(int x, int y) {
std::cout << "foo excuted" << std::endl;
return x + y;
}
int main() {
std::packaged_task<int(int, int)> task(&foo);
std::future<int> fut = task.get_future();
std::thread(std::move(task), 10, 20).detach();
std::cout << "Waiting...\n";
fut.wait(); // 等待 foo 执行完成
std::cout << "Result: " << fut.get() << std::endl; // 获取结果
return 0;
}
该示例中,我们使用 std::packaged_task
封装了一个 foo
函数,并创建了一个 std::future
实例来获取函数的执行结果。接着我们创建了一个 std::thread
的实例来异步执行 foo
函数,最后在主线程中调用 fut.wait()
等待函数执行的完成,并通过 fut.get()
获取函数的执行结果。示例中的输出结果如下:
Waiting...
foo excuted
Result: 30
2.2 使用示例2:使用 std::packaged_task 实现简单的线程池
在这个示例中,我们将使用一个 std::queue
来存储 std::packaged_task
实例,当有任务加入队列时,我们将创建一个新的线程来执行该任务,并从队列中移除该任务。该示例中的线程池实现采用了简单的线程安全措施,避免了多个线程同时操作队列产生竞争条件。
#include <iostream>
#include <thread>
#include <future>
#include <functional>
#include <queue>
#include <mutex>
#include <condition_variable>
int foo(int x, int y) {
std::cout << "foo excuted" << std::endl;
return x + y;
}
class ThreadPool {
public:
ThreadPool(size_t num_threads) {
for (int i = 0; i < num_threads; ++i) {
workers.emplace_back([this] {
for (;;) {
std::packaged_task<void()> task;
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock, [this] {
return this->stop || !this->tasks.empty();
});
if (this->stop && this->tasks.empty()) {
return;
}
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
}
template<typename Func, typename... Args>
auto enqueue(Func&& func, Args&&... args)
-> std::future<typename std::result_of<Func(Args...)>::type> {
using return_type = typename std::result_of<Func(Args...)>::type;
std::packaged_task<return_type()> task(
std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
);
std::future<return_type> fut = task.get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
// 禁止在停止线程池之后加入队列
if (stop) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks.emplace(std::move(task));
}
condition.notify_one();
return fut;
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for (std::thread& 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);
std::vector<std::future<int>> results;
for (int i = 0; i < 8; ++i) {
results.emplace_back(pool.enqueue(foo, i, i + 1));
}
for (auto&& result : results) {
std::cout << result.get() << ' ';
}
std::cout << std::endl;
return 0;
}
在该示例中,我们定义了一个名为 ThreadPool
的类来表示线程池。其中,构造函数接受一个整数表示线程池的大小,我们用 std::vector<std::thread>
来存储线程池中的所有线程。每个线程不断地从任务队列中取得任务,然后执行任务。
在 ThreadPool
中,我们定义了一个模板成员函数 enqueue
来将任务加入队列。该函数接受一个任意可调用的参数作为要执行的任务,函数将返回一个 std::future
对象,以便调用方获取任务的执行结果。
在主函数中,我们创建了一个 ThreadPool
实例,然后向线程池中加入了8个任务,每个任务都是 foo
函数的调用,并将任务的返回值存储在一个 std::future
对象中。最后,我们遍历所有结果并输出。
该示例中的输出结果如下:
foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
foo excuted
1 3 5 7 9 11 13 15
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++11中std::packaged_task的使用详解 - Python技术站