C++ 简单线程池

今天看了github上点击量最大的一个C++2.0实现的线程池:链接,感觉看起来很简单明了
于是看后自己默写了一个线程池类,并对里面步骤进行注释,也是提升我自己的一部分
tips:原代码中的std::result_ofC++17的类型萃取库中已经移除,因此我改用指代更为清晰的decltype进行修改。

具体代码如下,代码风格采用谷歌式:
这大概是CSDN上面最优雅的C++线程池代码了吧

#pragma once

#include <atomic>
#include <condition_variable>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>

class ThreadPool final {
 public:
  ThreadPool(uint8_t _num = 0);
  ~ThreadPool();
    
  ThreadPool(ThreadPool const&) = delete;
  ThreadPool(ThreadPool&&) = delete;
  ThreadPool& operator=(ThreadPool const&) = delete;
  ThreadPool& operator=(ThreadPool&&) = delete;

  // 函数模板声明在class体中,调用处实例化
  // 采用返回值后置,因为前置时形参还没声明,无法推导类型
  template <class F, class... Args>
  auto enqueue(F&& _func, Args&&... _args)
      -> std::future<decltype(_func(_args...))> {
    if (this->stop_.load()) {
      throw std::runtime_error("enqueue on stopped ThreadPool");
    }
    
    // 假如入队的函数为:int(int, char*, std::string)
    // decltype(_func(_args...))表示函数执行后返回值的类型
    // 那么 using rtype = int
    using rtype = decltype(_func(_args...));

    // 通过std::bind创造返回值相同的无参函数
    // 使用std::forward对入参进行完美转发
    // 从而得到对函数的包装对象:std::packaged_task<int()>
    auto task = std::make_shared<std::packaged_task<rtype()>>(
        std::bind(std::forward<F>(_func), std::forward<Args>(_args)...));

    // std::future<int>
    std::future<rtype> res = task->get_future();

    {
      std::lock_guard<std::mutex> lock(mtx_);
      
      // 由于tasks_中调用对象类型为void()
      // 构造一个返回值为void且无参数的匿名函数,在函数体中执行类型为int()的函数
      this->tasks_.emplace([task]() { (*task)(); });
    }
      
    // 不敢保证notify_one只会通知一个消费者而不会通知到自己,暂时我稍作改动,采取惊群
    this->cv_.notify_all();
    return res;
  }

 private:
  std::atomic<bool> stop_;
  std::vector<std::thread> threads_;
  std::queue<std::function<void()>> tasks_;
  std::mutex mtx_;
  std::condition_variable cv_;
};
#include "thread_pool.hpp"

ThreadPool::ThreadPool(uint8_t _num) : stop_{false} {
  for (auto i{0}; i != _num; ++i) {
    this->threads_.emplace_back([this] {
      while (true) {
        auto task = std::function<void()>();

        {
          std::unique_lock<std::mutex> lock(this->mtx_);
		 // 停止时不阻塞,运行时任务队列非空时不阻塞
          this->cv_.wait(lock, [this] {
            if (this->stop_.load() or !this->tasks_.empty()) {
              return true;
            }
            return false;
          });
	
          // 保证受到停止信号后,消费完所有任务
          if (this->stop_.load() and this->tasks_.empty()) {
            break;
          }

          task = std::move(this->tasks_.front());
          this->tasks_.pop();
        }

        task();
      }
    });
  }
}

ThreadPool::~ThreadPool() {
  this->stop_.store(true);
  this->cv_.notify_all();

  for (auto&& thread : this->threads_) {
    if (thread.joinable()) {
      thread.join();
    }
  }
}

运用:

#include "thread_pool.hpp"
 
using namespace std::literals;

int main() {
  // 根据硬件支持的最大并发数创建线程池
  auto pool = new ThreadPool(std::thread::hardware_concurrency());
  auto func = [](int a, int b) -> int { return std::pow(a, b); };
  auto futures = std::vector<std::future<int>>();

  for (auto i{0}; i != 10; ++i) {
    futures.push_back(pool->enqueue(func, i, 2));
  }

  // do something
  std::this_thread::sleep_for(10ms);

  for (auto&& future : futures) {
    std::cout << future.get() << std::endl;
  }
  delete pool;

  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++中实现线程池的基本思路是创建一个固定大小的线程池(线程数量等于处理器数量),然后将需要执行的任务放入任务队列中,线程池中的线程从任务队列中取出任务进行执行。 以下是一个简单C++线程池实现代码: ```c++ #include <iostream> #include <vector> #include <queue> #include <thread> #include <mutex> #include <condition_variable> #include <functional> using namespace std; class ThreadPool { public: ThreadPool(size_t num_threads) { for (size_t i = 0; i < num_threads; ++i) { threads.emplace_back([this]{ while (true) { function<void()> task; { unique_lock<mutex> lock(m); cv.wait(lock, [this]{ return !tasks.empty(); }); task = move(tasks.front()); tasks.pop(); } task(); } }); } } ~ThreadPool() { { unique_lock<mutex> lock(m); done = true; } cv.notify_all(); for (auto& thread : threads) { thread.join(); } } template<typename F, typename... Args> void enqueue(F&& f, Args&&... args) { auto task = make_shared<packaged_task<void()>>(bind(forward<F>(f), forward<Args>(args)...)); { unique_lock<mutex> lock(m); tasks.push([task]{ (*task)(); }); } cv.notify_one(); } private: vector<thread> threads; queue<function<void()>> tasks; mutex m; condition_variable cv; bool done = false; }; ``` 在上面的代码中,使用了一个vector来保存线程池中的所有线程,使用一个队列来保存所有需要执行的任务。enqueue函数用于向任务队列中添加一个新任务。 每个线程都会从任务队列中取出任务进行执行,如果队列为空,则线程会一直等待直到队列中有任务。线程池中的所有线程在析构函数中会被join掉,确保所有任务都被执行完毕。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

歪锅锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值