21xrx.com
2025-04-28 03:17:40 Monday
文章检索 我的文章 写文章
使用C++ std::future的线程池
2023-06-27 02:31:26 深夜i     62     0
C++ std::future 线程池 多线程 异步编程
return !tasks.empty() || stop;

C++中的std::future是一种很方便的异步编程方式,它可以让你在一个线程中执行一些任务,并在另外的线程中获取结果。但如果我们要大量地使用std::future时,就需要引入一个线程池来管理这些异步任务的执行。

一个简单的线程池通常由一个任务队列和一些工作线程组成。我们可以将任务加入队列中,工作线程会从队列中取出任务并执行。如果任务队列为空,工作线程就会进入等待状态。这样可以减少线程的创建和销毁,提高程序的效率。

下面是一个简单的使用C++ std::future的线程池实现:

#include <iostream>
#include <queue>
#include <vector>
#include <thread>
#include <mutex>
#include <future>
#include <functional>
class ThreadPool {
public:
  ThreadPool(int size = std::thread::hardware_concurrency());
  ~ThreadPool();
  template<class F, class... Args>
  auto submit(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;
  int size() const;
private:
  // task queue
  std::queue<std::function<void()>> tasks;
  // worker threads
  std::vector<std::thread> workers;
  // mutex to control access to the task queue
  std::mutex queue_mutex;
  // condition variable to notify workers of new task
  std::condition_variable queue_condition;
  bool stop;
};
ThreadPool::ThreadPool(int size) : stop(false) {
  for (int i = 0; i < size; i++) {
    workers.emplace_back(
      // lambda function which dequeues tasks and runs them
      [this] {
        while (!stop) {
          std::unique_lock<std::mutex> lock(queue_mutex);
          queue_condition.wait(lock, [this] { return !tasks.empty() || stop; });
          if (stop)
            return;
          
          std::function<void()> task = std::move(tasks.front());
          tasks.pop();
          lock.unlock();
          task();
        }
      }
    );
  }
}
ThreadPool::~ThreadPool() {
  {
    std::unique_lock<std::mutex> lock(queue_mutex);
    stop = true;
  }
  queue_condition.notify_all();
  for (auto& worker : workers) {
    worker.join();
  }
}
template<class F, class... Args>
auto ThreadPool::submit(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
  using return_type = typename std::result_of<F(Args...)>::type;
  auto task = std::make_shared<std::packaged_task<return_type()>>(
    std::bind(std::forward<F>(f), std::forward<Args>(args)...)
  );
  std::future<return_type> result = task->get_future();
  {
    std::unique_lock<std::mutex> lock(queue_mutex);
    if (stop) {
      throw std::runtime_error("submit on stopped ThreadPool");
    }
    tasks.emplace([task] { (*task)(); });
  }
  queue_condition.notify_one();
  return result;
}
int ThreadPool::size() const {
  return workers.size();
}

这个线程池有一个submit()成员函数,用于提交任务。它接受一个函数对象和它的参数,并返回一个std::future对象。当任务完成时,可以通过std::future获取函数执行的结果。

我们使用std::packaged_task类封装任务,这样可以让任务具有可调用对象的特性。submit()函数内部会构造一个std::shared_ptr <>>并将它保存在任务队列中,并返回它的std::future对象。

为了支持不同类型的函数和参数,我们使用了C++11的可变模板参数和std::result_of类型萃取技术。

高效地使用线程池要注意任务的分配和合理地设定线程数。如果任务相对比较短且数量很多,线程数设定大一些可以提高性能。如果任务数量不多,线程数设定小一些可以节省资源。

在实际使用线程池时,还需要处理异常以及线程池的析构顺序等问题。但总的来说,线程池可以让我们用一种很方便的方式加速程序的执行。

  
  

评论区

    相似文章
请求出错了