【Linux】线程池封装与介绍


🔥 个人主页:大耳朵土土垚 🔥 所属专栏:Linux系统编程
这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉

文章目录

1. 线程池介绍

之前我们实现了线程、互斥量、条件变量以及日志的封装,现在我们可以基于以上内容来封装一个线程池。

线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

线程池的应用场景:

  • 需要大量的线程来完成任务,且完成任务的时间比较短。 比如WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象⼀个热门网站的点击次数。 但对于长时间的任务,比如⼀个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

  • 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

  • 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

线程池的种类:

  • 创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执行任务对象中的任务接口;
  • 浮动线程池,其他同上。

此处,我们选择固定线程个数的线程池。

2. 线程池封装

  • 首先我们需要包含需要的头文件以及命名空间,线程池类中成员变量需要一把锁、条件变量、条件变量下等待的线程个数、存放线程的数组、线程总个数、存放任务的任务队列以及线程池是否在运行的状态表示:
cpp 复制代码
#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <memory>
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
namespace ThreadPoolModule
{
    using namespace LogModule;
    using namespace MutexModule;
    using namespace CondModule;
    using namespace ThreadModule;

    using thread_t = std::shared_ptr<Thread>;

    const static int gthreadnum = 10;

    template <typename T>
    class ThreadPool
    {
    private:
        int _threadnum; // 线程个数
        Mutex _mutex;
        Cond _cond;
        int _waitnum;
        std::vector<thread_t> _threads; // 存放线程
        std::queue<T> _tasks;           // 任务队列
        bool _isrunning;
    };
}

因为任务种类的不确定性,所以我们需要使用模板

  • 在线程池类的构造函数中我们就可以创建固定数量的线程并给每个线程绑定执行的方法:
cpp 复制代码
template <typename T>
    class ThreadPool
    {
    private:
        bool IsEmpty() { return _tasks.empty(); }
        void HandlerTask(std::string name)
        {
            LOG(LogLevel::INFO) << name << " HandlerTask...";
            T task;
            while (true)
            {
                {
                LockGuard lockguard(_mutex);

                while (IsEmpty() && _isrunning)
                {
                    ++_waitnum;
                    _cond.Wait(_mutex);
                    --_waitnum;
                }
                if(!_isrunning&&IsEmpty())//为了在退出之前处理完所有的任务
                    break;
                task = _tasks.front();
                _tasks.pop();
                }

                //处理任务不需要加锁保护
                task(name);
                LOG(LogLevel::INFO) << name << " handled a task successfully...";

            }
            LOG(LogLevel::INFO) << name << " exit success...";

        }

    public:
        // 是要有的,必须是私有的
        ThreadPool(int threadnum = gthreadnum) : _threadnum(threadnum),
                                                 _waitnum(0),
                                                 _isrunning(false)
        {
            // 创建线程
            for (int i = 0; i < _threadnum; i++)
            {
                _threads.emplace_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1))); // HandlerTask是成员函数有this参数,所以不能直接传参需要绑定
                LOG(LogLevel::INFO) << _threads.back()->Name() << "Create Success...";
            }
            LOG(LogLevel::INFO) << "ThreadPool Construct...";
        }

        ~ThreadPool()
        {
        }

    private:
        int _threadnum; // 线程个数
        Mutex _mutex;
        Cond _cond;
        int _waitnum;
        std::vector<thread_t> _threads; // 存放线程
        std::queue<T> _tasks;           // 任务队列
        bool _isrunning;
    };
}

如果不清楚线程类中函数与线程池中的函数接口之间是怎么相互调用的可以看看:

  • 线程Start、Stop与Wait:
cpp 复制代码
        void Start()
        {
            if (_isrunning)
                return;
            _isrunning = true;
            for (auto &thread : _threads)
            {
                thread->Start();
                LOG(LogLevel::INFO) << thread->Name() << " Start Success...";
            }
        }
        void Stop()
        {
            LockGuard lockguard(_mutex);
            if(_isrunning)
            {
                // 3. 不能在入任务了
                _isrunning = false; // 不工作
                // 1. 让线程自己退出(要唤醒) && // 2. 历史的任务被处理完了
                if(_waitnum>0)
                    _cond.NotifyAll();
            }
        }
        void Wait()
        {
            for (auto &thread : _threads)
            {
                thread->Join();
                LOG(LogLevel::INFO) << thread->Name() << "Wait Success...";
            }
        }
       
       

注意在线程池Stop这里,我们不能直接让线程退出,需要将线程池任务队列中所有任务处理完成后才能退出,所以我们可以借助条件变量唤醒所有等待的线程让它们去处理任务,等任务处理完成它们也就会随着函数执行完成后退出;然后就可以进行线程等待。

  • 线程池处理任务在上述HandlerTask中,我们还需要线程池插入新任务的方法:
cpp 复制代码
bool Enqueue(const T &in)
        {
            // 插入任务
            LockGuard lockguard(_mutex);
            if (!_isrunning)
                return false;
            _tasks.push(in);
            if (_waitnum > 0)
                _cond.Notify();
            return true;
        }

插入任务与处理任务一样访问了共享资源,所以需要加锁保护

完整实现

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <memory>
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
namespace ThreadPoolModule
{
    using namespace LogModule;
    using namespace MutexModule;
    using namespace CondModule;
    using namespace ThreadModule;

    using thread_t = std::shared_ptr<Thread>;

    const static int gthreadnum = 10;

    template <typename T>
    class ThreadPool
    {
    private:
        bool IsEmpty() { return _tasks.empty(); }
        void HandlerTask(std::string name)
        {
            LOG(LogLevel::INFO) << name << " HandlerTask...";
            T task;
            while (true)
            {
                {
                LockGuard lockguard(_mutex);

                while (IsEmpty() && _isrunning)
                {
                    ++_waitnum;
                    _cond.Wait(_mutex);
                    --_waitnum;
                }
                if(!_isrunning&&IsEmpty())
                    break;
                task = _tasks.front();
                _tasks.pop();
                }

                //处理任务不需要加锁保护
                task(name);
                LOG(LogLevel::INFO) << name << " handled a task successfully...";

            }
            LOG(LogLevel::INFO) << name << " exit success...";

        }

    public:
        // 是要有的,必须是私有的
        ThreadPool(int threadnum = gthreadnum) : _threadnum(threadnum),
                                                 _waitnum(0),
                                                 _isrunning(false)
        {
            // 创建线程
            for (int i = 0; i < _threadnum; i++)
            {
                _threads.emplace_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1))); // HandlerTask是成员函数有this参数,所以不能直接传参需要绑定
                LOG(LogLevel::INFO) << _threads.back()->Name() << "Create Success...";
            }
            LOG(LogLevel::INFO) << "ThreadPool Construct...";
        }

        void Start()
        {
            if (_isrunning)
                return;
            _isrunning = true;
            for (auto &thread : _threads)
            {
                thread->Start();
                LOG(LogLevel::INFO) << thread->Name() << " Start Success...";
            }
        }
        void Stop()
        {
            LockGuard lockguard(_mutex);
            if(_isrunning)
            {
                // 3. 不能在入任务了
                _isrunning = false; // 不工作
                // 1. 让线程自己退出(要唤醒) && // 2. 历史的任务被处理完了
                if(_waitnum>0)
                    _cond.NotifyAll();
            }
        }
        void Wait()
        {
            for (auto &thread : _threads)
            {
                thread->Join();
                LOG(LogLevel::INFO) << thread->Name() << "Wait Success...";
            }
        }
        bool Enqueue(const T &in)
        {
            // 插入任务
            LockGuard lockguard(_mutex);
            if (!_isrunning)
                return false;
            _tasks.push(in);
            if (_waitnum > 0)
                _cond.Notify();
            return true;
        }
        ~ThreadPool()
        {
        }

    private:
        int _threadnum; // 线程个数
        Mutex _mutex;
        Cond _cond;
        int _waitnum;
        std::vector<thread_t> _threads; // 存放线程
        std::queue<T> _tasks;           // 任务队列
        bool _isrunning;
    };
}
  • 测试函数如下:

任务函数:

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <functional>
#include "Log.hpp"

using namespace LogModule;

using task_t = std::function<void(std::string name)>;

void Push(std::string name)
{
    LOG(LogLevel::DEBUG) << "我是一个推送数据到服务器的一个任务, 我正在被执行" << "[" << name << "]";
}

main函数:

cpp 复制代码
#include "ThreadPool.hpp"
#include"Task.hpp"
using namespace ThreadPoolModule;

int main()
{
    ENABLE_CONSOLE_LOG_STRATEGY();
    // ENABLE_FILE_LOG_STRATEGY();

    std::unique_ptr<ThreadPool<task_t>> tp = std::make_unique<ThreadPool<task_t>>();
    tp->Start();

    int cnt = 10;
    char c;
    while (cnt)
    {
        tp->Enqueue(Push);
         cnt--;
        sleep(1);
    }

    tp->Stop();

    sleep(3);

    tp->Wait();

    return 0;
}
  • 结果如下:
cpp 复制代码
./thread_pool 
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-1Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-2Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-3Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-4Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-5Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-6Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-7Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-8Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-9Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [68] - Thread-10Create Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [70] - ThreadPool Construct...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-1 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-2 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-3 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-4 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-5 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-6 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-7 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-8 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-9 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [81] - Thread-10 Start Success...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-7 HandlerTask...
[2025-02-04 09:54:51 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-7]
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-7 handled a task successfully...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-8 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-9 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-6 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-10 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-5 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-4 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-3 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-2 HandlerTask...
[2025-02-04 09:54:51 ][INFO] [2360325] [ThreadPool.hpp] [30] - Thread-1 HandlerTask...
[2025-02-04 09:54:52 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-7]
[2025-02-04 09:54:52 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-7 handled a task successfully...
[2025-02-04 09:54:53 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-8]
[2025-02-04 09:54:53 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-8 handled a task successfully...
[2025-02-04 09:54:54 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-9]
[2025-02-04 09:54:54 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-9 handled a task successfully...
[2025-02-04 09:54:55 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-6]
[2025-02-04 09:54:55 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-6 handled a task successfully...
[2025-02-04 09:54:56 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-10]
[2025-02-04 09:54:56 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-10 handled a task successfully...
[2025-02-04 09:54:57 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-5]
[2025-02-04 09:54:57 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-5 handled a task successfully...
[2025-02-04 09:54:58 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-4]
[2025-02-04 09:54:58 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-4 handled a task successfully...
[2025-02-04 09:54:59 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-3]
[2025-02-04 09:54:59 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-3 handled a task successfully...
[2025-02-04 09:55:00 ][DEBUG] [2360325] [Task.hpp] [14] - 我是一个推送数据到服务器的一个任务, 我正在被执行[Thread-2]
[2025-02-04 09:55:00 ][INFO] [2360325] [ThreadPool.hpp] [51] - Thread-2 handled a task successfully...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-7 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-8 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-9 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-6 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-10 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-5 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-4 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-3 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-2 exit success...
[2025-02-04 09:55:01 ][INFO] [2360325] [ThreadPool.hpp] [54] - Thread-1 exit success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-1Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-2Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-3Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-4Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-5Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-6Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-7Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-8Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-9Wait Success...
[2025-02-04 09:55:04 ][INFO] [2360325] [ThreadPool.hpp] [101] - Thread-10Wait Success...

3. 线程安全的单例模式

单例模式的特点

某些类, 只应该具有⼀个对象(实例), 就称之为单例。

例如⼀个男⼈只能有⼀个媳妇。

在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中. 此时往往要用一个单例的类来管理这些数据。

饿汉实现方式和懒汉实现方式

洗碗的例子

  • 吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭.
  • 吃完饭, 先把碗放下, 然后下⼀顿饭用到这个碗了再洗碗, 就是懒汉方式.

懒汉方式最核心的思想是 "延时加载". 从而能够优化服务器的启动速度.

  • 饿汉方式实现单例模式:
cpp 复制代码
template <typename T>
class Singleton {
	static T data;
public:
	static T* GetInstance() {
		return &data;
		}
};

只要通过 Singleton 这个包装类来使用 T 对象, 则⼀个进程中只有⼀个 T 对象的实例.

  • 懒汉方式实现单例模式:
cpp 复制代码
template <typename T>
class Singleton {
   static T* inst;

public:
   static T* GetInstance() {
   	if (inst == NULL)
   	 {
   		inst = new T();
   	 }
   	return inst;
   	}
};

这种方式存在⼀个严重的问题, 线程不安全。第一次调用 GetInstance 的时候, 如果两个线程同时调用, 可能会创建出两份 T 对象的实例。但是后续再次调用, 就没有问题了。与之前未加锁的简易抢票示例相似。

  • 懒汉方式实现单例模式(线程安全版本):
cpp 复制代码
// 懒汉模式, 线程安全
template <typename T>
class Singleton {
	volatile static T* inst; // 需要设置 volatile 关键字, 否则可能被编译器优化.
	static std::mutex lock;
public:
	static T* GetInstance() 
	{
		if (inst == NULL) // 双重判定空指针, 降低锁冲突的概率, 提⾼性能.
		{ 
			lock.lock(); // 使⽤互斥锁, 保证多线程情况下也只调⽤⼀次 new.
			if (inst == NULL) 
			{
				inst = new T();
			}
			lock.unlock();
		 }
	return inst;
	}
};

注意事项:

  1. 加锁解锁的位置
  2. 双重 if 判定, 避免不必要的锁竞争

如果inst不是NULL就没必要加锁

  1. volatile关键字防止过度优化

4. 单例式线程池

我们使用的是饿汉实现方式来实现单例线程池,首先我们需要创建静态全局的线程池指针以及锁:

cpp 复制代码
#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <memory>
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
namespace ThreadPoolModule
{
    using namespace LogModule;
    using namespace MutexModule;
    using namespace CondModule;
    using namespace ThreadModule;

    using thread_t = std::shared_ptr<Thread>;

    const static int gthreadnum = 10;

    template <typename T>
    class ThreadPool
    {
    private:
        int _threadnum; // 线程个数
        Mutex _mutex;
        Cond _cond;
        int _waitnum;
        std::vector<thread_t> _threads; // 存放线程
        std::queue<T> _tasks;           // 任务队列
        bool _isrunning;

	// 添加单例模式
	static ThreadPool<T> *_instance;
	static Mutex _lock;
};
//在类外初始化
template <typename T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;
template <typename T>
Mutex ThreadPool<T>::_lock;
}
  • 然后获取线程池单例函数:
cpp 复制代码
static ThreadPool<T> *GetInstance()
        {
            if (_instance == nullptr)
            {
                LockGuard lockguard(_lock);
                if (_instance == nullptr)
                {
                    _instance = new ThreadPool<T>();
                    _instance->Start();
                }
            }
            LOG(LogLevel::DEBUG) << "GetInstance success...";
            return _instance;
        }
  • 最后将构造函数以及Start函数设为私有以及将赋值和拷贝构造禁用。

完整实现

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <memory>
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
namespace ThreadPoolModule
{
    using namespace LogModule;
    using namespace MutexModule;
    using namespace CondModule;
    using namespace ThreadModule;

    using thread_t = std::shared_ptr<Thread>;

    const static int gthreadnum = 10;

    template <typename T>
    class ThreadPool
    {
    private:
        // 是要有的,必须是私有的
        ThreadPool(int threadnum = gthreadnum) : _threadnum(threadnum),
                                                 _waitnum(0),
                                                 _isrunning(false)
        {
            // 创建线程
            for (int i = 0; i < _threadnum; i++)
            {
                _threads.emplace_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1))); // HandlerTask是成员函数有this参数,所以不能直接传参需要绑定
                LOG(LogLevel::INFO) << _threads.back()->Name() << "Create Success...";
            }
            LOG(LogLevel::INFO) << "ThreadPool Construct...";
        }
        bool IsEmpty() { return _tasks.empty(); }
        void HandlerTask(std::string name)
        {
            LOG(LogLevel::INFO) << name << " HandlerTask...";
            T task;
            while (true)
            {
                {
                    LockGuard lockguard(_mutex);

                    while (IsEmpty() && _isrunning)
                    {
                        ++_waitnum;
                        _cond.Wait(_mutex);
                        --_waitnum;
                    }
                    if (!_isrunning && IsEmpty())
                        break;
                    task = _tasks.front();
                    _tasks.pop();
                }

                // 处理任务不需要加锁保护
                task(name);
                LOG(LogLevel::INFO) << name << " handled a task successfully...";
            }
            LOG(LogLevel::INFO) << name << " exit success...";
        }
        void Start()
        {
            if (_isrunning)
                return;
            _isrunning = true;
            for (auto &thread : _threads)
            {
                thread->Start();
                LOG(LogLevel::INFO) << thread->Name() << " Start Success...";
            }
        }
        // 赋值、拷⻉禁⽤
        ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
        ThreadPool(const ThreadPool<T> &) = delete;

    public:
        static ThreadPool<T> *GetInstance()
        {
            if (_instance == nullptr)
            {
                LockGuard lockguard(_lock);
                if (_instance == nullptr)
                {
                    _instance = new ThreadPool<T>();
                    _instance->Start();
                }
            }
            LOG(LogLevel::DEBUG) << "GetInstance success...";
            return _instance;
        }

        void Stop()
        {
            LockGuard lockguard(_mutex);
            if (_isrunning)
            {
                // 3. 不能在入任务了
                _isrunning = false; // 不工作
                // 1. 让线程自己退出(要唤醒) && // 2. 历史的任务被处理完了
                if (_waitnum > 0)
                    _cond.NotifyAll();
            }
        }
        void Wait()
        {
            for (auto &thread : _threads)
            {
                thread->Join();
                LOG(LogLevel::INFO) << thread->Name() << "Wait Success...";
            }
        }
        bool Enqueue(const T &in)
        {
            // 插入任务
            LockGuard lockguard(_mutex);
            if (!_isrunning)
                return false;
            _tasks.push(in);
            if (_waitnum > 0)
                _cond.Notify();
            return true;
        }
        ~ThreadPool()
        {
        }

    private:
        int _threadnum; // 线程个数
        Mutex _mutex;
        Cond _cond;
        int _waitnum;
        std::vector<thread_t> _threads; // 存放线程
        std::queue<T> _tasks;           // 任务队列
        bool _isrunning;

        // 单例模式
        static ThreadPool<T> *_instance;
        static Mutex _lock;
    };
    // 在类外初始化
    template <typename T>
    ThreadPool<T> *ThreadPool<T>::_instance = nullptr;
    template <typename T>
    Mutex ThreadPool<T>::_lock;
}
  • 测试函数:
cpp 复制代码
#include <iostream>
#include <functional>
#include <unistd.h>
#include "ThreadPool.hpp"
using namespace ThreadPoolModule;

using task_t = std::function<void(std::string name)>;
void DownLoad(std::string name)
{
    std::cout << "this is a task" << std::endl;
}
int main()
{
    ENABLE_CONSOLE_LOG_STRATEGY();
    int cnt = 10;
    while (cnt)
    {
        ThreadPool<task_t>::GetInstance()->Enqueue(DownLoad);
        sleep(1);
        cnt--;
    }
    ThreadPool<task_t>::GetInstance()->Stop();
    sleep(5);
    ThreadPool<task_t>::GetInstance()->Wait();
    return 0;
}
  • 结果如下:
cpp 复制代码
tutu@hecs-16648:~/linux/ThreadPool$ ./thread_pool 
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-1Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-2Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-3Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-4Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-5Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-6Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-7Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-8Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-9Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [36] - Thread-10Create Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [38] - ThreadPool Construct...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-1 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-2 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-3 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-4 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-5 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-6 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-7 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-8 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-9 Start Success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [76] - Thread-10 Start Success...
[2025-02-04 14:40:58 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-7 HandlerTask...
this is a task
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-8 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-7 handled a task successfully...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-6 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-9 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-10 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-5 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-4 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-3 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-2 HandlerTask...
[2025-02-04 14:40:58 ][INFO] [2368011] [ThreadPool.hpp] [43] - Thread-1 HandlerTask...
[2025-02-04 14:40:59 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:40:59 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-8 handled a task successfully...
[2025-02-04 14:41:00 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:00 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-7 handled a task successfully...
[2025-02-04 14:41:01 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:01 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-6 handled a task successfully...
[2025-02-04 14:41:02 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:02 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-9 handled a task successfully...
[2025-02-04 14:41:03 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:03 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-10 handled a task successfully...
[2025-02-04 14:41:04 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:04 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-5 handled a task successfully...
[2025-02-04 14:41:05 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:05 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-4 handled a task successfully...
[2025-02-04 14:41:06 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:06 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-3 handled a task successfully...
[2025-02-04 14:41:07 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
this is a task
[2025-02-04 14:41:07 ][INFO] [2368011] [ThreadPool.hpp] [64] - Thread-2 handled a task successfully...
[2025-02-04 14:41:08 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-8 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-7 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-6 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-9 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-10 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-5 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-4 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-3 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-2 exit success...
[2025-02-04 14:41:08 ][INFO] [2368011] [ThreadPool.hpp] [66] - Thread-1 exit success...
[2025-02-04 14:41:13 ][DEBUG] [2368011] [ThreadPool.hpp] [95] - GetInstance success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-1Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-2Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-3Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-4Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-5Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-6Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-7Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-8Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-9Wait Success...
[2025-02-04 14:41:13 ][INFO] [2368011] [ThreadPool.hpp] [116] - Thread-10Wait Success...
相关推荐
码农101号17 分钟前
Linux中容器文件操作和数据卷使用以及目录挂载
linux·运维·服务器
我是一只代码狗26 分钟前
springboot中使用线程池
java·spring boot·后端
PanZonghui35 分钟前
Centos项目部署之Nginx 的安装与卸载
linux·nginx
hello早上好39 分钟前
JDK 代理原理
java·spring boot·spring
PanZonghui42 分钟前
Centos项目部署之安装数据库MySQL8
linux·后端·mysql
PanZonghui43 分钟前
Centos项目部署之运行SpringBoot打包后的jar文件
linux·spring boot
PanZonghui44 分钟前
Centos项目部署之Java安装与配置
java·linux
程序员弘羽1 小时前
Linux进程管理:从基础到实战
linux·运维·服务器
PanZonghui1 小时前
Centos项目部署之常用操作命令
linux
JeffersonZU1 小时前
Linux/Unix进程概念及基本操作(PID、内存布局、虚拟内存、环境变量、fork、exit、wait、exec、system)
linux·c语言·unix·gnu