返回

基于C++11的线程池实现: 细枝末节暗藏玄机,初学者必读!

后端

前段时间偶然在github上看到了一个非常精炼的C++11线程池实现。作者用不到100行的代码实现了一个简单的线程池,且使用了较多C++11的新特性。于是便想着自己也来动手看看,思路上基本是借鉴原版,在部分实现上做了些许调整。

线程池简介

线程池是一种管理线程的机制,它可以将线程集中起来,以便在需要时可以快速分配线程来执行任务。线程池可以提高应用程序的性能,因为它可以减少创建和销毁线程的开销。

C++11线程池实现

以下是用C++11实现的一个简单的线程池:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

// 线程池类
class ThreadPool {
public:
  // 构造函数
  ThreadPool(int num_threads) : num_threads_(num_threads) {
    // 创建线程池中的线程
    for (int i = 0; i < num_threads_; i++) {
      threads_.push_back(std::thread([this] {
        while (true) {
          // 获取任务
          std::function<void()> task;
          {
            std::unique_lock<std::mutex> lock(mtx_);
            // 如果任务队列为空,则等待任务
            while (tasks_.empty()) {
              cv_.wait(lock);
            }
            // 获取任务
            task = std::move(tasks_.front());
            // 从任务队列中移除任务
            tasks_.pop();
          }
          // 执行任务
          task();
        }
      }));
    }
  }

  // 析构函数
  ~ThreadPool() {
    // 停止线程池中的线程
    {
      std::unique_lock<std::mutex> lock(mtx_);
      stop_ = true;
      cv_.notify_all();
    }
    // 等待线程池中的线程退出
    for (auto& thread : threads_) {
      thread.join();
    }
  }

  // 添加任务到线程池
  void AddTask(std::function<void()> task) {
    // 将任务添加到任务队列中
    {
      std::unique_lock<std::mutex> lock(mtx_);
      tasks_.push(std::move(task));
      // 通知线程池中的线程有任务可执行
      cv_.notify_one();
    }
  }

private:
  // 线程池中线程的数量
  int num_threads_;
  // 线程池中的线程
  std::vector<std::thread> threads_;
  // 任务队列
  std::queue<std::function<void()>> tasks_;
  // 互斥锁
  std::mutex mtx_;
  // 条件变量
  std::condition_variable cv_;
  // 停止标志
  bool stop_ = false;
};

// 使用线程池
int main() {
  // 创建一个线程池
  ThreadPool thread_pool(4);

  // 添加任务到线程池
  for (int i = 0; i < 100; i++) {
    thread_pool.AddTask([i] {
      std::cout << "任务" << i << "正在执行\n";
    });
  }

  return 0;
}

这个线程池实现使用了C++11的许多新特性,如lambda表达式、move语义和原子操作。这些特性可以提高线程池的性能和简化代码。

使用线程池的注意事项

在使用线程池时,需要注意以下几点:

  • 线程池的大小应根据应用程序的实际需要来确定。如果线程池过大,可能会导致线程资源的浪费;如果线程池过小,可能会导致任务执行速度变慢。
  • 线程池中的线程应尽量保持忙碌。如果线程池中的线程经常处于空闲状态,可能会导致线程资源的浪费。
  • 线程池中的任务应尽量避免长时间运行。如果任务运行时间过长,可能会导致线程池中的其他任务无法及时执行。

使用范例

线程池可以用于各种并行计算场景。例如,可以使用线程池来处理图像处理、视频编码、数据分析等任务。

以下是一个使用线程池处理图像处理任务的示例:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace cv;

// 线程池类
class ThreadPool {
public:
  // 构造函数
  ThreadPool(int num_threads) : num_threads_(num_threads) {
    // 创建线程池中的线程
    for (int i = 0; i < num_threads_; i++) {
      threads_.push_back(std::thread([this] {
        while (true) {
          // 获取任务
          std::function<void()> task;
          {
            std::unique_lock<std::mutex> lock(mtx_);
            // 如果任务队列为空,则等待任务
            while (tasks_.empty()) {
              cv_.wait(lock);
            }
            // 获取任务
            task = std::move(tasks_.front());
            // 从任务队列中移除任务
            tasks_.pop();
          }
          // 执行任务
          task();
        }
      }));
    }
  }

  // 析构函数
  ~ThreadPool() {
    // 停止线程池中的线程
    {
      std::unique_lock<std::mutex> lock(mtx_);
      stop_ = true;
      cv_.notify_all();
    }
    // 等待线程池中的线程退出
    for (auto& thread : threads_) {
      thread.join();
    }
  }

  // 添加任务到线程池
  void AddTask(std::function<void()> task) {
    // 将任务添加到任务队列中
    {
      std::unique_lock<std::mutex> lock(mtx_);
      tasks_.push(std::move(task));
      // 通知线程池中的线程有任务可执行
      cv_.notify_one();
    }
  }

private:
  // 线程池中线程的数量
  int num_threads_;
  // 线程池中的线程
  std::vector<std::thread> threads_;
  // 任务队列
  std::queue<std::function<void()>> tasks_;
  // 互斥锁
  std::mutex mtx_;
  // 条件变量
  std::condition_variable cv_;
  // 停止标志
  bool stop_ = false;
};

// 图像处理任务
void ImageProcessingTask(Mat image) {
  // 对图像进行处理
  cvtColor(image, image, COLOR_BGR2GRAY);
  GaussianBlur(image, image, Size(3, 3), 0);
  Canny(image, image, 50, 100);
}

// 使用线程池处理图像处理任务
int main() {
  // 创建一个线程池
  ThreadPool thread_pool(4);

  // 读取图像
  Mat image = imread("image.jpg");

  // 将图像处理任务添加到线程池
  thread_pool.AddTask([image] {
    ImageProcessingTask(image);
    imwrite("processed_image.jpg", image);
  });

  return 0;
}

在这个示例中,我们使用线程池来处理图像处理任务。首先,我们创建一个线程池。然后,我们将图像处理任务添加到线程池。最后,我们等待线程池中的线程完成任务。

结语

线程池是一种非常有用的工具,它可以提高应用程序的性能。C++11为线程池的实现提供了许多新的特性,这些特性可以使线程池的实现更加简单和高效。