21xrx.com
2024-12-22 21:31:27 Sunday
登录
文章检索 我的文章 写文章
如何用c++实现线程池?
2023-07-09 22:33:15 深夜i     --     --
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

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

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复