Linux之线程池与单例模式

目录

线程池

线程池代码

单例模式

饿汉模式单例模式

懒汉模式单例模式


在前几期,我们已经学习了多线程的创建和控制,学习了多线程中的同步和互斥,学习了多线程中的条件变量和信号量,基于此我们实现了基于阻塞队列和基于环形队列的生产者和消费者模型。本期将在此基础上进一步拓展学习,学习线程池相关的内容。

线程池

在学习线程池之前,我们通过一个场景为大家引入。图示如下。

当我们用户在用户层使用malloc函数和new操作符进行内存空间的申请时,必须由操作系统在底层使用对应的系统调用接口进行内存的申请,具体步骤为,进程由用户态切换为内核态,然后在内核态通过对应的内存算法进行内存的申请。但不免有一种情况,用户频繁的申请大小为1MB的空间,操作系统在底层不断地使用内存算法申请小块空间,在这种情景下,频繁地使用内存置换算法申请小块空间的代价是非常大的,效率也非常的低。基于此我们事先会通过操作系统在底层通过内存算法申请一大块空间,这样用户在申请时,可以直接从操作系统事先申请好的空间中去申请,不用再让操作系统频繁地使用内存算法申请小块空间,大大提高了效率。所以,内存池也是类似的原理,最终为提高了代码的执行效率。
内存池: 提前准备好的线程,用来随时处理任务,我们就称作线程池。

线程池代码

创建一个可以处理多个任务的线程池。

ThreadPool.hpp

cpp 复制代码
#pragma once
#include <queue>
#include <pthread.h>
#include <iostream>

using namespace std;

namespace threadpool
{
    const int g_num = 5;

    template <class T>
    class ThreadPool
    {
    public:
        ThreadPool(const int &num = g_num) : _num(num)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }
        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }

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

        void Lock()
        {
            pthread_mutex_lock(&_mutex);
        }

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

        void Pop(T *out)
        {

            *out = _task_queue.front();
            _task_queue.pop();
        }

        void Push(const T &data)
        {
            Lock();
            _task_queue.push(data);
            Unlock();
            Wakeup();
        }
        void Wakeup()
        {
            pthread_cond_signal(&_cond);
        }
        static void *Rountine(void *args)
        {
            // 线程分离,分线程退出时不用主线程去等待。
            pthread_detach(pthread_self());
            ThreadPool<T> *tp = (ThreadPool<T> *)args;
               
            while (true)
            {
                 tp->Lock();
                // 分线程去处理任务
                while (tp->_task_queue.empty())
                {
                    tp->Wait();
                }
                T t;
                tp->Pop(&t);
                tp->Unlock();
                std::cout<<pthread_self()  << "得到的数据为 " << t << std::endl;
            }
        }

        void InitThreadPool()
        {
            pthread_t tid;
            for (int i = 0; i < g_num; i++)
            {
                pthread_create(&tid, nullptr, Rountine, (void *)this);
            }
        }

    private:
        // 存放任务的队列
        queue<T> _task_queue;
        // 表示线程池中线程的数目
        int _num;
        pthread_mutex_t _mutex;
        pthread_cond_t _cond;
    };

}

test.cc

cpp 复制代码
#include "ThreadPool.hpp"
#include <iostream>
#include <unistd.h>
#include <time.h>
using namespace std;
using namespace threadpool;

int main()
{
    ThreadPool<int> tp;
    tp.InitThreadPool();
    // 随机数种子
    srand((long long)time(nullptr));
    while (true)
    {
        int a=rand()%20;
        tp.Push(a);
        cout << "发送的数据为" << a << endl;
        sleep(1);
    }

    return 0;
}

运行结果如下。

由运行结果可知,运行结果符合预期。

单例模式

在学习单例模式之前,我们了解一下什么是设计模式,模式其实就是特定的场景给予特定的解决方案。在人类社会中,成熟的行业都会有成熟的设计模式。何为单例模式,单例模式其实就是一个类只允许实例化出一个对象的设计模式。

饿汉模式单例模式

饿汉就是,工资日结。

懒汉模式单例模式代码。

cpp 复制代码
template <class T>
class SigDistance
{
private:
    static SigDistance<T> _t;

public:
    static SigDistance<T> *GetDistance()
    {
        return &_t;
    }
};

懒汉模式单例模式

懒汉就是,工资月结。

cpp 复制代码
template <class T>
class SigDistance
{
private:
    static SigDistance<T> *_t;
public:
    static SigDistance<T> *GetDistance()
    {
        if (_t == nullptr)
        {
            _t=new SigDistance();
        }
        return _t;
    }
};

SigDistance<T>* SigDistance<T>::_t = nullptr;

基于懒汉单例模式的线程池

线程池往往只有一个,所以我们把线程池类设计成了单例模式,代码如下。

Thread.hpp

cpp 复制代码
#pragma once
#include <queue>
#include <pthread.h>
#include <iostream>

using namespace std;

namespace threadpool
{
    const int g_num = 5;

    template <class T>
    class ThreadPool
    {
    private:
        ThreadPool<T>(const int &num = g_num) : _num(num)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }
        ~ThreadPool<T>()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }

        ThreadPool<T>(const ThreadPool<T> &tp) = delete;
        ThreadPool<T> &operator=(const ThreadPool<T> &tp) = delete;
        void Wait()
        {
            pthread_cond_wait(&_cond, &_mutex);
        }

        void Lock()
        {
            pthread_mutex_lock(&_mutex);
        }

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

    public:
        void Pop(T *out)
        {

            *out = _task_queue.front();
            _task_queue.pop();
        }

        void Push(const T &data)
        {
            Lock();
            _task_queue.push(data);
            Unlock();
            Wakeup();
        }
        void Wakeup()
        {
            pthread_cond_signal(&_cond);
        }
        static ThreadPool<T> *GetDistance()
        {
            static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
   
            if(_tp==nullptr)
            {
                pthread_mutex_lock(&lock);
                  if (_tp == nullptr)
                {
                    _tp = new ThreadPool<T>();
                }
            }
            _tp->InitThreadPool();
            pthread_mutex_unlock(&lock);
            return _tp;
        }
        static void *Rountine(void *args)
        {
            // 线程分离,分线程退出时不用主线程去等待。
            pthread_detach(pthread_self());
            ThreadPool<T> *tp = (ThreadPool<T> *)args;

            while (true)
            {
                tp->Lock();
                // 分线程去处理任务
                while (tp->_task_queue.empty())
                {
                    tp->Wait();
                }
                T t;
                tp->Pop(&t);
                tp->Unlock();
                std::cout << pthread_self() << "得到的数据为 " << t << std::endl;
            }
        }

        void InitThreadPool()
        {
            pthread_t tid;
            for (int i = 0; i < g_num; i++)
            {
                pthread_create(&tid, nullptr, Rountine, (void *)this);
            }
        }

    private:
        // 存放任务的队列
        queue<T> _task_queue;
        // 表示线程池中线程的数目
        int _num;
        static ThreadPool<T> *_tp;
        pthread_mutex_t _mutex;
        pthread_cond_t _cond;
    };
    template <class T>
    ThreadPool<T> *ThreadPool<T>::_tp = nullptr;
}

需要注意的是,静态成员变量需要再类外进行初始化。

test.cc

cpp 复制代码
#include "ThreadPool.hpp"
#include <iostream>
#include <unistd.h>
#include <time.h>
using namespace std;
using namespace threadpool;

int main()
{
    ThreadPool<int> *tp = ThreadPool<int>::GetDistance();
   
    // 随机数种子
    srand((long long)time(nullptr));
    while (true)
    {
        int a = rand() % 20;
        tp->Push(a);
        cout << "发送的数据为" << a << endl;
        sleep(1);
    }

    return 0;
}

运行结果如下。

运行结果符合预期。

以上便是线程池以及单例模式的所有内容。

本期内容到此结束^_^

相关推荐
jz_ddk3 分钟前
[LVGL] 从0开始,学LVGL:进阶应用与项目实战(上)
linux·信息可视化·嵌入式·gui·lvgl·界面设计
望获linux27 分钟前
【实时Linux实战系列】Linux 内核的实时组调度(Real-Time Group Scheduling)
java·linux·服务器·前端·数据库·人工智能·深度学习
MC丶科38 分钟前
【SpringBoot常见报错与解决方案】端口被占用?Spring Boot 修改端口号的 3 种方法,第 3 种 90% 的人不知道!
java·linux·spring boot
江公望1 小时前
ubuntu kylin(优麒麟)和标准ubuntu的区别浅谈
linux·服务器·ubuntu·kylin
Lynnxiaowen1 小时前
今天我们开始学习python语句和模块
linux·运维·开发语言·python·学习
生态笔记1 小时前
PPT宏代码
linux·服务器·powerpoint
mucheni1 小时前
迅为RK3588开发板Ubuntu 系统开发ubuntu终端密码登录
linux·运维·ubuntu
skywoodsky2 小时前
Ubuntu 24.04环境下的挂起转休眠
linux
小云数据库服务专线2 小时前
GaussDB 应用侧报Read timed out解决方法
linux·服务器·gaussdb
资源补给站2 小时前
服务器高效操作指南:Python 环境退出与 Linux 终端快捷键全解析
linux·服务器·python