linux下的单例安全的线程池实现

目录

一、引言

二、池化技术

三、代码实例

四、代码剖析

头文件和命名空间

结构体定义

模板类定义

私有成员函数(锁相关)

私有成员函数(队列相关)

公有成员函数(线程池操作)

单例模式实现

构造函数和析构函数

静态成员变量定义

静态成员变量初始化


一、引言

随着信息技术的飞速发展,服务器端应用程序面临着越来越高的并发处理需求。如何在保证系统稳定性和响应速度的同时,有效管理线程资源,成为了软件开发中的一个重要课题。单例模式的线程池,作为一种高效管理线程资源的设计模式,在Linux环境下得到了广泛的应用。本文将探讨如何在Linux系统中实现一个线程安全的单例线程池,确保全局只有一个线程池实例,并提供线程安全的数据访问和任务调度,从而为高并发、高可用性的服务端程序奠定坚实的基础。以下是单例安全线程池的实现要点和步骤。

二、池化技术

线程池包括:push到线程池的任务 2.多个准备处理任务的线程

存在一批线程所以一定:互斥 + 同步

因此需要使用锁和条件变量

三、代码实例

cpp 复制代码
#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>

using namespace std;

struct ThreadInfo
{
    pthread_t tid;
    string name;
};


static const int defalutnum = 5;

template<class T>
class ThreadPool
{
private:    //一些私有的关于锁的线程函数
    void Lock()
    {
        pthread_mutex_lock(&_mutex);
    } 

    void Unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    void Wait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    void Wake()
    {
        pthread_cond_signal(&_cond);
    }

private:    
    bool IsQueueEmpty()
    {
        return _tasks.empty();
    }

    string GetThreadName(pthread_t tid)     //根据线程ID,获取线程名
    {
        for (aoto& info : _threads)
        {
            if (info.tid == tid)
            {
                return info.name;
            }
        }

        return "No such thread";    
    }

public:
    void start()   //启动线程池
    {
        int num = _threads.size();
        for (int i = 0; i < num; i++)
        {
            pthread_create(&_threads[i].tid, nullptr, HandlerTask, this);      //传this,让ThredHandldler函数变成静态(减少一个this参数)
            _threads[i].name = "Thread-" + to_string(i + 1);
        }
    }

    T Pop()
    {
        T t = _tasks.front();   //类型就是任务,取出队头任务
        _tasks.pop();
        return t;   
    }

    void Push(const T& t)
    {
        Lock();
        _tasks.push(t);
        Wake();     //(没有任务之后,进行wait)push之后就可以唤醒线程
        Unlock();
    }
    /*
    当等待队列有数据时(即有至少一个线程正在等待这个条件变量):
    pthread_cond_signal 调用成功时,它会唤醒等待该条件变量的一个线程(在多个线程等待的情况下,具体唤醒哪一个线程取决于线程调度策略和实现)。
    返回值通常是 0,表示成功。

    当等待队列没有数据时(即没有线程正在等待这个条件变量):
    pthread_cond_signal 调用仍然会成功执行,但是因为没有线程在等待,所以实际上没有线程被唤醒。
    返回值仍然是 0,表示成功。
    */

   static ThreadPool<T>* GetInstance()   //单例模式(一般静态成员会提供静态方法)
   {
        if (_instance == nullptr)       //防止重复加锁
        {
            pthread_mutex_lock(&_lock);
            if (_instance == nullptr)
            {
                std::cout << "log: singleton create done first!" << std::endl;
                _instance = new ThreadPool<T>();    //加锁申请
            }
            pthread_mutex_unlock(&_lock);
        }

        return _instance;
   }

private:    //默认成员函数
    ThreadPool()
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        _threads.resize(defalutnum);   //默认创建5个线程
    }

    ~ThreadPool()   
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        //全局锁和条件变量不需要销毁
        //STL自定义类型不用管
        // 内置类型不用管
    }  

    ThreadPool<T>& operator=(const ThreadPool<T>& t1) = delete;   //禁止拷贝构造
    
    ThreadPool(const ThreadPool<T>& t1) = delete;   //禁止拷贝构造



private: 
    vector<ThreadInfo> _threads;    //线程信息
    queue<T> _tasks;           //任务队列

    pthread_mutex_t _mutex;     //控制内部线程的互斥锁
    pthread_cond_t _cond;       //控制内部线程的条件变量

    static ThreadPool<T>* _instance;   //单例模式
    static pthread_mutex_t _lock;   //实例化单例时,进行保护的专门的锁
};


template<class T>   //使用模板时,需要点名模板,点名类域时,需要加上模板参数(模板实例化之后才是类)
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;   //单例模式

template<class T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;   //实例化单例时,进行保护的专门的锁

需要注意的是

1.线程的执行方法只能有一个参数void * arg

但是由于是类内方法,所以会有一个隐藏的this指针。为了避免多出一个参数,我们选择将方法变成静态,这样手动去传入this指针。

2.这是懒汉模式的单例,为了创建单例时,每次都不需要申请锁,这样外部额外包裹了一层判断,这层判断可以避免已经申请好单例之后(指针不为空),直接避免进入if内容,从而直接返回。

3.在pop的时候没有加锁,那么调用pop时就得手动加锁。

四、代码剖析

头文件和命名空间

cpp 复制代码
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>
using namespace std;
  • #pragma once:防止头文件被多次包含。
  • 包含了必要的标准库头文件,如<iostream>, <vector>, <string>, <queue>, <pthread.h>, <unistd.h>
  • 使用了std命名空间

结构体定义

struct ThreadInfo {

pthread_t tid;

string name;

};

  • ThreadInfo结构体用于存储线程信息,包括线程ID (tid) 和线程名称 (name)。

模板类定义

template<class T>

class ThreadPool {

// ...

};

私有成员函数(锁相关)

cpp 复制代码
private:    //一些私有的关于锁的线程函数
void Lock() { pthread_mutex_lock(&_mutex); } 
void Unlock() { pthread_mutex_unlock(&_mutex); }
void Wait() { pthread_cond_wait(&_cond, &_mutex); }
void Wake() { pthread_cond_signal(&_cond); }
  • LockUnlock用于加锁和解锁互斥锁。
  • Wait用于等待条件变量。
  • Wake用于唤醒等待条件变量的线程。

私有成员函数(队列相关)

cpp 复制代码
private:    
bool IsQueueEmpty() { return _tasks.empty(); }
string GetThreadName(pthread_t tid) {
    for (auto& info : _threads) {
        if (info.tid == tid) {
            return info.name;
        }
    }
    return "No such thread";    
}
  • IsQueueEmpty检查任务队列是否为空。
  • GetThreadName根据线程ID获取线程名称。

公有成员函数(线程池操作)

cpp 复制代码
public:
void start() {
    int num = _threads.size();
    for (int i = 0; i < num; i++) {
        pthread_create(&_threads[i].tid, nullptr, HandlerTask, this);      //传this,让ThredHandldler函数变成静态(减少一个this参数)
        _threads[i].name = "Thread-" + to_string(i + 1);
    }
}
T Pop() {
    T t = _tasks.front();   //类型就是任务,取出队头任务
    _tasks.pop();
    return t;   
}
void Push(const T& t) {
    Lock();
    _tasks.push(t);
    Wake();     //(没有任务之后,进行wait)push之后就可以唤醒线程
    Unlock();
}
  • start启动线程池,创建指定数量的线程并初始化线程信息。
  • Pop从任务队列中取出一个任务。
  • Push将任务添加到任务队列,并唤醒等待的线程。

单例模式实现

cpp 复制代码
static ThreadPool<T>* GetInstance() {
    if (_instance == nullptr) {       //防止重复加锁
        pthread_mutex_lock(&_lock);
        if (_instance == nullptr) {
            std::cout << "log: singleton create done first!" << std::endl;
            _instance = new ThreadPool<T>();    //加锁申请
        }
        pthread_mutex_unlock(&_lock);
    }
    return _instance;
}
  • GetInstance方法实现单例模式,确保全局只有一个线程池实例。

构造函数和析构函数

cpp 复制代码
private:    //默认成员函数
ThreadPool() {
    pthread_mutex_init(&_mutex, nullptr);
    pthread_cond_init(&_cond, nullptr);
    _threads.resize(defalutnum);   //默认创建5个线程
}
~ThreadPool() {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
}  
ThreadPool<T>& operator=(const ThreadPool<T>& t1) = delete;   //禁止拷贝构造
ThreadPool(const ThreadPool<T>& t1) = delete;   //禁止拷贝构造
  • 构造函数初始化互斥锁、条件变量,并设置默认线程数量。
  • 析构函数销毁互斥锁和条件变量。
  • 禁止拷贝构造和赋值操作。

静态成员变量定义

cpp 复制代码
private:    //默认成员函数
vector<ThreadInfo> _threads;    //线程信息
queue<T> _tasks;           //任务队列
pthread_mutex_t _mutex;     //控制内部线程的互斥锁
pthread_cond_t _cond;       //控制内部线程的条件变量
static ThreadPool<T>* _instance;   //单例模式
static pthread_mutex_t _lock;   //实例化单例时,进行保护的专门的锁
  • _threads存储线程信息。
  • _tasks存储任务队列。
  • _mutex_cond分别用于同步访问任务队列。
  • _instance是单例模式的实例指针。
  • _lock用于保护单例实例的创建过程。

静态成员变量初始化

cpp 复制代码
template<class T>   //使用模板时,需要点名模板,点名类域时,需要加上模板参数(模板实例化之后才是类)
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;   //单例模式
template<class T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;   //实例化单例时,进行保护的专门的锁
  • 初始化静态成员变量。
相关推荐
孫治AllenSun几秒前
【shell】常用100个shell命令使用讲解
java·linux·服务器
.生产的驴4 分钟前
SpringBoot 开启热部署 项目热启动 一键调试无需 无需重启
java·运维·开发语言·spring boot·后端·spring·eclipse
kaoa00013 分钟前
Linux入门攻坚——42、Nginx及web站点架构模式
linux·前端·nginx
上海运维Q先生32 分钟前
面试题整理3----nc命令的常见用法
linux
KawYang33 分钟前
xxl-job 整合 Seatunnel 实现定时任务
linux·服务器·数据库
白帽黑客26591 小时前
入门网络安全工程师要学习哪些内容【2025年寒假最新学习计划】
linux·python·web安全·网络安全·系统安全·密码学·ddos
德迅云安全杨德俊1 小时前
云服务器防御DDOS的方案
服务器·安全·web安全·ddos
打码人的日常分享1 小时前
【系统测试文档】系统测试计划,系统测试报告书,测试方案,测试记录,测试用例(Word原件)
运维·安全·系统安全·测试用例·需求分析·规格说明书
陈小也~1 小时前
VMware虚拟机Ubuntu 18.04版本 磁盘扩容
linux·ubuntu
-指短琴长-2 小时前
Linux从0到1——线程自定义封装
linux·运维·c++