线程池项目优化

[复制链接]
发表于 2025-10-20 22:00:42 | 显示全部楼层 |阅读模式
1.假如用户提交使命都用一个类是不是比力贫困?能不能用函数

作为用户来说,确实我想利用线程池,还要创建一个类,那样可太累了,以是我想直接放入函数就能运行,好比

如许利用是不是更加方便呢,但是具体怎么实现呢?
这里我们可以看到反面的两个参数是这个函数的两个形参可以想到模板参数
下面给出完备代码
  1. template<typename Func, typename... Args>
  2. auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))>
  3. {
  4.      if (!isPoolRunning_) {
  5.          std::cerr << "Thread pool is shutdown, cannot submit task." << std::endl;
  6.          using RType = decltype(func(args...));
  7.          auto dummyTask = std::make_shared<std::packaged_task<RType()>>(
  8.              []() -> RType { return RType(); }
  9.          );
  10.          (*dummyTask)();
  11.          return dummyTask->get_future();
  12.      }
  13.      // 打包任务,放入任务队列里面
  14.      using RType = decltype(func(args...));
  15.      auto task = std::make_shared<std::packaged_task<RType()>>(
  16.          std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
  17.      );
  18.      std::future<RType> result = task->get_future();
  19.      // 获取锁
  20.      std::unique_lock<std::mutex> lock(taskQueMtx_);
  21.      // 用户提交任务,最长不能阻塞超过1s,否则判断提交任务失败,返回
  22.      if (!notFull_.wait_for(lock, std::chrono::seconds(1), [&] {
  23.          return taskQue_.size() < static_cast<size_t>(taskQueMaxThreshHold_);
  24.          })) {
  25.          // 表示notFull_等待1s种,条件依然没有满足
  26.          std::cerr << "task queue is full, submit task fail." << std::endl;
  27.          auto dummyTask = std::make_shared<std::packaged_task<RType()>>(
  28.              []() -> RType { return RType(); }
  29.          );
  30.          (*dummyTask)();
  31.          return dummyTask->get_future();
  32.      }
  33.      // 如果有空余,把任务放入任务队列中
  34.      taskQue_.emplace([task]() { (*task)(); });
  35.      taskSize_++;
  36.      // 因为新放了任务,任务队列肯定不空了,在notEmpty_上进行通知,赶快分配线程执行任务
  37.      notEmpty_.notify_all();
  38.      // cached模式 任务处理比较紧急 场景:小而快的任务 需要根据任务数量和空闲线程的数量,判断是否需要创建新的线程出来
  39.      if (poolMode_ == PoolMode::MODE_CACHED
  40.          && taskSize_ > idleThreadSize_
  41.          && curThreadSize_ < threadSizeThreshHold_)
  42.      {
  43.          std::cout << ">>> create new thread..." << std::endl;
  44.          // 创建新的线程对象
  45.          auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
  46.          int threadId = ptr->getId();
  47.          threads_.emplace(threadId, std::move(ptr));
  48.          // 启动线程
  49.          threads_[threadId]->start();
  50.          // 修改线程个数相关的变量
  51.          curThreadSize_++;
  52.          idleThreadSize_++;
  53.      }
  54.      // 返回任务的Result对象
  55.      return result;
  56. }
复制代码
这里的     auto submitTask(Func&& func, Args&&... args) -> std::future<decltype(func(args...))>

  • typename Func:这是一个模板范例参数,体现传入的使命函数的范例。Func 可以是任何可调用对象,如平常函数、函数指针、成员函数指针、lambda 表达式、std::function 对象等。
  • typename... Args:这是一个可变参数模板,... 体现可以担当恣意数目和范例的参数。Args 是这些参数的范例聚集。
以是这里和我们调用的情势刚刚好,这里用了引用折叠,参数会被完善转发给使命函数。

  • decltype(func(args...)):decltype 是 C++ 的一个范例推导关键字,用于在编译时推导表达式的范例。func(args...) 体现调用使命函数 func 并传入参数 args...,decltype(func(args...)) 会推导出这个调用的返回范例。因此,std::future<decltype(func(args...))> 体现返回一个 std::future 对象,该对象可以获取使命函数的返回结果。
这里接纳了异步的方式,和我们之前的利用大同小异,这里显得更加方便,同时异步的方式可以获取抛非常,我们之前假如获取到其他的值也直接放回了
  1. if (!isPoolRunning_) {
  2.     std::cerr << "Thread pool is shutdown, cannot submit task." << std::endl;
  3.     using RType = decltype(func(args...));
  4.     auto dummyTask = std::make_shared<std::packaged_task<RType()>>(
  5.         []() -> RType { return RType(); }
  6.     );
  7.     (*dummyTask)();
  8.     return dummyTask->get_future();
  9. }
复制代码
对于这段代码来说,假如不能运行了,就返回一个空对象出去,这里是通过packaged_task这个和futuer搭配利用,它的get_futuer会返回一个futuer对象
  1. // 打包任务,放入任务队列里面
  2. using RType = decltype(func(args...));
  3. auto task = std::make_shared<std::packaged_task<RType()>>(
  4.     std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
  5. );
  6. std::future<RType> result = task->get_future();
复制代码
这里就是假如不是空,那么肯定是有参数的,那么这里就须要把参数绑定到函数中,以是哟用了bind函数
同时利用了完善转发生存了参数的左值照旧右值属性 
这里的  std::future<RType> result = task->get_future();j仅仅是获取一个对象,并没有实行使命

2.线程池的优雅制止

这里由于想到了假如我们在利用线程池,使命都完成了,我想把线程池通过一个信号把它竣事,大概它在运行我一样也想竣事它,那么就得优雅制止下来,否则整个步调就非常制止了,大概会有内存走漏

怎么计划呢?,这里我利用了一个shutdown函数,用户自己调用,就可以实现线程池的优雅制止

这里是利用了shutdonwn()的,可以看到下面另有三个使命,值都是0
假如倒霉用的话

每个使命都会实行完毕 
  1. void shutdown(bool wait = true)
  2. {
  3.      {
  4.          std::unique_lock<std::mutex> lock(taskQueMtx_);
  5.          if (!isPoolRunning_) return;
  6.          isPoolRunning_ = false;
  7.          // 等待所有任务完成
  8.          if (wait) {
  9.              while (taskSize_ > 0 || executingTaskCount_ > 0) {
  10.                  taskFinished_.wait(lock);
  11.              }
  12.          }
  13.          // 清空队列
  14.          while (!taskQue_.empty())
  15.          {
  16.              taskQue_.pop();
  17.              taskSize_--;
  18.          }
  19.          notEmpty_.notify_all();
  20.      }
  21.      std::unique_lock<std::mutex> lock(taskQueMtx_);
  22.      exitCond_.wait(lock, [this] { return threads_.empty(); });
  23. }
复制代码
这是这个函数的告急计划
这里用了一个wait,假如直接调用,那么就会是true,这里加了一个使命完成的原子范例,executingTaskCount_ 假如使命大概没有实行的使命那么直接往下走,假如有,那么就期待,线程实行一个使命就会查抄是不是为0,假如是0就关照taskFinished,拿到锁然后清空使命队列,关照全部线程,开释锁,这里各个线程看到线程池的isPoolRunning_ 是false就会知道关闭了,就会退出线程队列,这里会期待全部线程退出,然后竣事shutdown函数,如许就实现了优雅制止


3.收尾

实在内里另有许多细节的改动,好比使命队列的包装什么的,但是这是两个比力大的改动,这里实在另有其他地方可以改动,好比许多地方可以用到c++20的语法,把该项目彻底改成c++20的项目,另有就是固定模式和动态厘革的模式可以融合起来,如许省去了利用的贫困,就加一个条件就行了,



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表