21xrx.com
2025-06-26 17:52:12 Thursday
文章检索 我的文章 写文章
如何使用C++中的std::future来实现线程池?
2023-07-07 06:08:29 深夜i     47     0
C++ std::future 线程池 实现
return this->stop || !this->tasks.empty();

在多线程编程中,线程池被广泛应用以提高程序的并发性和性能。而C++中的std::future则是一种很好的实现线程池的方式。本文将介绍如何使用C++中的std::future来实现线程池。

首先,我们需要创建一个线程池类,该类包含一个队列和一组线程和一个互斥量,确保线程安全。这个类应该包含公共接口,允许客户端线程提交任务到线程池。

在C++11标准中,std::async函数可以用来创建异步任务,返回一个std::future对象,我们可以通过该对象获取异步任务的执行结果。接着,我们可以将这些std::future对象存储在任务队列中,并在空闲线程中执行它们。当线程池中的线程变为空闲状态时,它们就可以处理该队列中正在等待执行的任务。

下面是一个简单的线程池类的实现,采用std::future来实现异步任务的执行:

#include <future>
#include <queue>
#include <vector>
#include <functional>
#include <thread>
#include <mutex>
#include <condition_variable>
class ThreadPool
{
public:
  ThreadPool(size_t numThreads)
    : stop(false)
  {
    for (size_t i = 0; i < numThreads; ++i)
    {
      workers.emplace_back(
        [this]
        {
          for (;;)
          {
            std::function<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<class F, class... Args>
  auto enqueue(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> res = task->get_future();
    {
      std::unique_lock<std::mutex> lock(queue_mutex);
      if (stop)
        throw std::runtime_error("enqueue on stopped ThreadPool");
      tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return res;
  }
  ~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::function<void()>> tasks;
  std::mutex queue_mutex;
  std::condition_variable condition;
  bool stop;
};

在上面的代码中,我们创建了一个包含多个线程的线程池,并且在ThreadPool类中定义了一个enqueue函数来向任务队列中添加任务。该enqueue函数接受一个可调用对象和可调用对象的参数,并将它们作为一个任务压入任务队列中,返回一个std::future对象,可用于获取异步任务的返回值。线程池中的每个线程都会反复地从队列中获取任务并执行它们,直到线程池停止运行。

我们可以使用如下代码来使用ThreadPool类:

#include <iostream>
#include <chrono>
void foo(int n)
{
  std::this_thread::sleep_for(std::chrono::seconds(1));
  std::cout << "Result = " << n * 2 << std::endl;
}
int main()
{
  ThreadPool tp(4);
  std::cout << "Enqueuing tasks..." << std::endl;
  for (int i = 0; i < 8; ++i)
  {
    auto result = tp.enqueue(foo, i);
  }
  std::this_thread::sleep_for(std::chrono::seconds(5));
  std::cout << "Done!" << std::endl;
  return 0;
}

在上面的代码中,我们首先创建一个ThreadPool对象,它包含4个线程。接着,我们使用enqueue函数将8个任务添加到线程池中。每个任务在执行时都休眠1秒钟,并将其参数乘以2作为结果输出。我们在主线程中休眠5秒钟来确保所有的任务都执行完毕。最后,我们输出“Done!”以表示程序的结束。

总结来说,使用C++中的std::future来实现线程池是一种高效的多线程编程方式。通过使用std::future,我们可以轻松地获取异步任务的返回值,并避免手动管理线程和锁,从而减少了错误的可能性。使用上述代码实现的ThreadPool类是一个很好的参考实现,可以用于快速实现线程池。

  
  

评论区