cpp线程池的实现

希望我们不仅把编程当成一份工作,更要将其当成一份热爱!!!

                                                                                                                                       -----潜意识中有个想成为一名厉害的程序员的梦

什么是线程池

       就是在一个池子(一个类)里有几个已经被创建的线程(线程就是用来执行代码的执行流),他们不断的检测任务队列是否有新任务(任务实际上就是代码块、即函数),当队列中有新任务时,空闲线程将其取出并执行。且线程在执行完当前任务后,不会被销毁,可以继续等待队列中有新任务时,将其取出执行。

       典型的生产者消费者模式。

线程池的优势

  1. 当有新任务时,无需新建线程,利用线程池现存的空闲线程执行,减少新建线程的开销。
  2. 当有新任务到达,且存在空闲线程的时候,因为不用再新建线程,所以提高了响应时间。
  3. 线程执行完当前任务后,无需销毁,继续复用给未来的任务,减少销毁线程的开销。

线程池类的实现

核心成员变量

  •   线程集合(采用c++11标准库的thread)
  •   任务队列(任务实际上指的就是函数,这里采用function来接收任务(函数))

核心方法

  • 构造函数:设置线程池线程数量,新建线程并绑定其工作函数。
  • 线程工作函数:循环检测队列是否有任务,有任务则提取进行执行(核心)。
  • 任务提交函数:提交任务到任务队列。
  • 停止函数:控制线程池的停止。
  • 析构函数:等待所有线程执行完毕。

代码如下:

/*
ClassName: threads_pool
Description : 手写简单线程池
Author : Select and Strive
Date : 2024 - 07 - 20
Version  1.0
to be improved:加入条件变量,避免循环造成的资源浪费
               动态化调整线程的数量
*/

#pragma once
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <functional>

using namespace std;

//生产者消费者模式
//核心类成员:线程集合,任务集合
//核心成员函数:线程工作函数(循环读取任务集合,并执行任务),任务集合提交函数

class threads_pool
{
public:
    threads_pool():threads_num(10), stop(false){
        init();
    }
    //设置线程的个数。
    threads_pool(unsigned int threads_num_param ):threads_num(threads_num_param ), stop(false) {
        init();
    }

    ~threads_pool(){
       //如果不在此处等待线程完毕,那么主线程结束后,所有线程池的子线程会被迫结束,导致任务没有被执行完毕。
       //加了这段代码,在主线程退出后,会调用线程池的析构函数,停在这里等待线程执行完任务后停止(前提时,stop函数必须被调用,否则线程不会结束)
        for (auto& temp:threads_vec)
        {
            temp.join();
        }
        
    }
//任务提交
void task_submit(function<void()> task)
 {
    mtx_q.lock();
    tasks_que.push(task);
    mtx_q.unlock();
 }

void pool_stop()//所有任务都已经提交后,才能调用此函数。
{
    //当队列为空,即所有任务都被线程取走的时候试着stop为true。这样当线程执行完所有任务后,即可停止。
    while (!tasks_que.empty()){}
    stop.store(true, memory_order_release);
}
private:
    //初始化
    void init()
    {
        //绑定线程的工作函数
        for (size_t i = 0; i < threads_num; i++)
        {
            threads_vec.emplace_back(&threads_pool::thread_RunTaskFromQueue,this);
        }
    }

    //线程工作函数
    void thread_RunTaskFromQueue()
    {
       
        while (true&&!stop.load(std::memory_order_acquire)) //利用.load(std::memory_order_acquire)方式可以保证从内存中读取数据,而不是从cpu缓存中读取数据
        {
            function<void()> temp(nullptr);
            mtx_q.lock();

            //如果非空,则提取任务。
            if(!tasks_que.empty())
            {
                temp = tasks_que.front();
                tasks_que.pop();
            }

            mtx_q.unlock();

            //如果提取了任务,则执行
            if (temp)
            {
                temp();
            }
        }
    }

    vector<thread>  threads_vec;//线程池集合
    queue<function<void()>>  tasks_que;//任务队列

    unsigned int threads_num;//线程池数量

    mutex mtx_q;//任务队列互斥锁
    atomic<bool> stop;//控制线程池的停止
};

 额外说明

  • 为了防止多线程同时访问共享资源(任务队列)出现问题,对其访问的时候要加互斥锁
  • 为了保证调用停止功能(设置stop变量为true)后,线程的工作函数能及时读到其更新后的值(数据可见性问题),采用 atomic<bool>来定义stop。利用.store(true, memory_order_release)方法来设置stop为true,可以保证更新stop的值后立即被写入内存,而不是cpu缓存(cpu本身的缓存策略,导致更改的值不会第一时间更新到内存,而是更新到缓存);利用.load(std::memory_order_acquire)方式可以保证从内存中读取数据,而不是从cpu缓存中读取数据(cpu的缓存机制会导致其从缓存中读取数据以至于不是最新的数据)。
  • 这里停止函数的实现逻辑是:首先停止函数是在所有任务都加入任务队列后才能被调用,这样在函数中检查任务队列为空,即所有任务都被线程所提取的时候,设置stop为true。那么当此时正在执行任务的线程执行完任务后,下一次循环时,检测stop为true则跳出循环,结束线程。所以调用完停止函数后,并不是立刻停止,而是等待所有任务全部执行完才会停止。
  • 如果没有停止函数的话,因为线程的工作函数是个死循环,那么它就会一直执行。
  • 在析构函数中一定要等待所有线程执行完毕,否则当主线程退出后,线程还没有将任务执行完,就会被迫退出。

测试代码如下:

#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <functional>
#include "threads_pool.h"

using namespace std;
std::mutex cout_mutex;
int main()
{
    threads_pool my_threads_pool(3);
	size_t i = 0;
	for (; i < 6; i++)
	{
		my_threads_pool.task_submit([i]()
			{
				cout_mutex.lock();
				cout << "任务" << i<<"正在运行" << endl;
				cout_mutex.unlock();
				std::this_thread::sleep_for(std::chrono::milliseconds(500));
				cout_mutex.lock();
				cout << "任务" << i << "运行结束" << endl;
				cout_mutex.unlock();
			}
		);
	}

	std::this_thread::sleep_for(std::chrono::seconds(5));
	for (; i < 12; i++)
	{
		my_threads_pool.task_submit([i]()
			{
				cout_mutex.lock();
				cout << "任务" << i << "正在运行" << endl;
				cout_mutex.unlock();
				std::this_thread::sleep_for(std::chrono::milliseconds(500));
				cout_mutex.lock();
				cout << "任务" << i << "运行结束" << endl;
				cout_mutex.unlock();
			}
		);
	}

	my_threads_pool.pool_stop();

}

待提高

  • 加入条件变量,避免循环造成的资源浪费
  • 动态化调整线程的数量
     
  • 17
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++实现线程池的基本步骤如下: 1. 创建一个线程池管理器 ThreadPoolManager,用于管理线程池的状态。 2. 创建一个队列,用于存储任务队列。 3. 创建一组线程,用于执行任务。 4. 将任务添加到任务队列中。 5. 当线程池中的线程被空闲下来时,从任务队列中取出任务并分配给空闲的线程进行执行。 6. 当任务队列为空时,线程池进入等待状态。 7. 当线程池被关闭时,停止所有的线程并清空任务队列。 下面是一个简单的C++线程池实现的代码: ```cpp #include <iostream> #include <queue> #include <thread> #include <mutex> #include <condition_variable> // 任务类 class Task { public: virtual void execute() = 0; }; // 线程池类 class ThreadPoolManager { public: ThreadPoolManager(int threadPoolSize) : poolSize(threadPoolSize), isShutdown(false) { // 创建一组线程,初始化线程池 for (int i = 0; i < poolSize; i++) { std::thread worker(&ThreadPoolManager::run, this); threads.push_back(std::move(worker)); } } ~ThreadPoolManager() { { std::unique_lock<std::mutex> lock(queueMutex); isShutdown = true; } condition.notify_all(); for (auto& thread : threads) { thread.join(); } } void execute(Task* task) { std::unique_lock<std::mutex> lock(queueMutex); taskQueue.push(task); condition.notify_one(); } private: void run() { while (true) { std::unique_lock<std::mutex> lock(queueMutex); condition.wait(lock, [this] { return !taskQueue.empty() || isShutdown; }); if (isShutdown) { break; } Task* task = taskQueue.front(); taskQueue.pop(); lock.unlock(); task->execute(); delete task; } } int poolSize; std::vector<std::thread> threads; std::queue<Task*> taskQueue; std::mutex queueMutex; std::condition_variable condition; bool isShutdown; }; // 自定义任务类 class MyTask : public Task { public: MyTask(int n) : num(n) {} void execute() { std::cout << "Thread " << std::this_thread::get_id() << " executing task " << num << std::endl; } private: int num; }; int main() { ThreadPoolManager pool(4); for (int i = 0; i < 10; i++) { pool.execute(new MyTask(i)); } return 0; } ``` 在上面的代码中,我们创建了一个线程池管理器 ThreadPoolManager 和一个任务类 Task。ThreadPoolManager 类中有一个任务队列 taskQueue 和一组线程 threads,我们可以通过调用 execute() 方法将任务添加到任务队列中。线程池中的每个线程都是一个 std::thread 类的实例,它从任务队列中取出任务并执行。当线程池被关闭时,我们需要调用析构函数( ~ThreadPoolManager()~ )来停止所有的线程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值