21xrx.com
2024-12-22 23:06:26 Sunday
登录
文章检索 我的文章 写文章
使用C++ std::future的线程池
2023-06-27 02:31:26 深夜i     --     --
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类型萃取技术。

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

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

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复