21xrx.com
2025-03-25 00:10:22 Tuesday
文章检索 我的文章 写文章
如何用c++实现线程池?
2023-07-09 22:33:15 深夜i     23     0
C++ 线程池 实现

              return !tasks_.empty() || running_ == false;

线程池是一种常见的并发编程工具,它可以复用一组线程来处理大量的任务,避免了线程频繁创建和销毁的开销。在高并发场景下,线程池可以显著提升程序性能和稳定性。本文介绍如何用c++实现线程池。

一、线程池的基本结构

线程池可以被看作是一个任务队列和一组工作线程的组合。对于任务队列,我们需要支持任务的添加和取出。对于工作线程,需要保证线程的数量和绑定的任务队列。假设线程池可以处理n个任务,m个线程并发执行,根据负载均衡的原则,一个线程在空闲时需要从任务队列中获取一个任务执行,这样可以让所有线程的负载尽可能平均。

二、线程池的实现

在c++11中,我们可以使用 等库进行线程池的开发,其中使用std::thread创建线程, 库用于线程间互斥和条件锁。下面是通过c++11库实现的基本结构:

#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
using namespace std;
class ThreadPool {
public:
  using Task = function<void()>;
  explicit ThreadPool(size_t thread_num = 8): pool_size_(thread_num) {
    Start();
  }
  ~ThreadPool() {
    Stop();
  }
  void AddTask(Task&& task) {
    {
      lock_guard<mutex> lock{task_mutex_};
      tasks_.emplace(move(task));
    }
    task_cv_.notify_one();
  }
private:
  void Start() {
    for (size_t i = 0; i < pool_size_; ++i) {
      workers_.emplace_back([this] {
        for (;;) {
          Task task;
          {
            unique_lock<mutex> lock{task_mutex_};
            task_cv_.wait(lock, [this] {
              return !tasks_.empty() || running_ == false;
            });
            if (!tasks_.empty()) {
              task = move(tasks_.front());
              tasks_.pop();
            }
          }
          if (task) {
            task();
          } else
            break;
          
        }
      });
    }
  }
  void Stop() noexcept {
    {
      lock_guard<mutex> lock{task_mutex_};
      running_ = false;
    }
    task_cv_.notify_all();
    for (auto& worker : workers_) {
      if (worker.joinable()) {
        worker.join();
      }
    }
  }
  vector<thread> workers_;
  queue<Task> tasks_;
  mutex task_mutex_;
  condition_variable task_cv_;
  size_t pool_size_;
  atomic<bool> running_{true};
};

该线程池实现的核心是Task任务和worker工作线程。每个任务是一个函数对象,通过queue进行任务队列的添加和取出。worker线程是一个无限循环的函数,不断从task队列中取任务,并执行任务。线程数和任务队列的长度可以在ThreadPool构造函数中设置,默认8个线程。

三、线程池的使用

使用线程池时,只需要创建一个ThreadPool对象,并调用AddTask方法添加任务。由于任务为函数对象,因此可以使用lambda表达式来简化代码。以下是使用线程池的示例代码:

void TestTask(int n)
  cout << "Task " << n << " is running" << endl;
int main() {
  ThreadPool pool{4};
  for (int i = 0; i < 10; ++i) {
    pool.AddTask([i] {
      TestTask(i);
    });
  }
  return 0;
}

输出结果为:

Task 0 is running
Task 2 is running
Task 1 is running
Task 3 is running
Task 8 is running
Task 4 is running
Task 5 is running
Task 7 is running
Task 6 is running
Task 9 is running

注意:由于线程池本身是异步执行,因此任务的输出顺序可能不一定是按照任务顺序的。

  
  

评论区

请求出错了