当前位置: 首页 > article >正文

Linux多线程(线程池与单例模式)

目录

一、内存池

二、线程池的实现

1.线程池

2.任务文件

3.主函数

三、单例模式

1.懒汉模式

2.饿汉模式

3.线程安全


一、内存池

当用户调用new等函数的时候,会向内核申请空间。每调用一次申请一次空间而且空间不够时还会执行内存算法等,需要花费时间。因此在程序创建之初,OS索性直接将一大块内存分配给用户。

内存池的目的是为了提高效率,线程池也同理。

因此我们可以类比处线程池的概念,即提前创建一批线程,以便于随时处理任务。

二、线程池的实现

一个线程池除了包含大量线程之外,还包含一个任务队列。有一个生产任务的线程将任务传入任务队列中,线程池中的线程从任务队列中拿到任务,并进行处理。

1.任务文件

#include<iostream>
using namespace std;
namespace ns_task
{
    class Task
    {
    private:
        int x_;
        int y_;
        char op_;
    public:
        Task(int x,int y,char op):x_(x),y_(y),op_(op)
        {};
        Task()
        {};
        int Run()
        {
            int res=0;
            switch(op_)
            {
            case '+':
                res=x_+y_;
                    break;
            case '-':
                res=x_-y_;
                    break;
            case '*':
                res=x_*y_;
                    break;
            case '/':
                res=x_/y_;
                    break;
            case '%':
                res=x_%y_;
                    break;
            default:
                cout<<"bug"<<endl;
                    break;
            }
            cout<<"当前任务正在被"<<pthread_self()<<"处理"<<x_<<op_<<y_<<"="<<res<<endl;
        }
    };
}

2.线程池

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<queue>
#include"Task.hpp"
using namespace ns_task;
namespace ns_threadpool
{
    template<class T>
    class ThreadPool
    {
        private:
        int num_;
        queue<T> task_queue_;
        pthread_mutex_t mtx;
        pthread_cond_t cond;
        public:
        ThreadPool(int num=5):num_(num)
        {
            pthread_mutex_init(&mtx,nullptr);
            pthread_cond_init(&cond,nullptr);
        }
        bool IsEmpty()
        {
            return task_queue_.empty();
        }
        void lock()
        {
            pthread_mutex_lock(&mtx);
        }
        void unlock()
        {
            pthread_mutex_unlock(&mtx);
        }
        void Wait()
        {
            pthread_cond_wait(&cond,&mtx);
        }
        void Wakeup()
        {
            pthread_cond_signal(&cond);
        }
        void PushTask(const T& in)
        {
            lock();
            task_queue_.push(in);
            unlock();
            Wakeup();
        }
        void PopTask(T* out)
        {
            *out=task_queue_.front();
            task_queue_.pop();
        }
        static void* Routine(void* args)
        {
            pthread_detach(pthread_self());
            ThreadPool<T>* tp=(ThreadPool<T>*)args;
            while(true)
            {
                tp->lock();
                if(tp->IsEmpty())
                {
                    tp->Wait();
                }
                T t;
                tp->PopTask(&t);
                tp->unlock();
                t.Run();
            }
        }
        void InitThreadPool()
        {
            pthread_t tid;
            for(int i=0;i<num_;i++)
            {
                pthread_create(&tid,nullptr,Routine,(void*)this);
            }
        }
        ~ThreadPool()
        {
            pthread_mutex_destroy(&mtx);
            pthread_cond_destroy(&cond);
        }
    };
}

3.主函数

#include"thread_pool.hpp"
using namespace ns_threadpool;
int main()
{
    ThreadPool<Task>* tp=new ThreadPool<Task>();
    srand((long long)time(nullptr));
    tp->InitThreadPool();
    while(true)
    {
        int x=rand()%20+1;
        int y=rand()%10+1;
        char arr[]="+-*/%";
        char op=arr[rand()%1+4];
        Task t(x,y,op);
        tp->PushTask(t);
        sleep(1);
    }
}

三、单例模式

单例模式即只让对象在内存中存在一份,即一个类之定义一个对象,对于线程池来说只有一个线程池就够了。因此线程池的定义可以使用单例模式。

一般而言,需要采用单例模式的情景是:

1.语义上只需要一个对象。

2.该对象内部存在大量空间保存大量数据,若存在多份(或各种拷贝),内存中就存在冗余数据。

1.懒汉模式

template<class T>
class Singleton
{
private:
    Singleton() {}
    Singleton(const Singleton<T>& a)=delete;
    Singleton<T>& operator=(Singleton<T>& a)=delete;
    static Singleton<T>* inst;
public:
    static T* GetInstance()
    {
         if(inst==NULL)
         {
             inst=new T();
         }
         return inst;
    }
};
template<class T>
Singleton<T>* Singleton<T>::inst=nullptr; 

懒汉式的做法是,当需要使用对象的时候再创建一个对象。

2.饿汉模式

template<class T>
class Singleton
{
ptivate:
    Singleton() {}
    Singleton(const Singleton& a)=delete;
    Singleton<T>& operator=(Singleton<T>& a)=delete;
    static Singleton data;
public:
    static Singleton<T>* GetInstance()
    {
        return &data;
    }
};
template <class T>
Singleton<T> Singleton<T>::data=0;

饿汉模式表现为,当创建这个类的时候,对象已经创建好了,可以随时使用。

3.线程安全

由于单例本身会再任何场景,任何环境下被调用。因此可能会导致GetInstance被重入而产生线程安全问题。

我们可以使用单例模式改写线程池的代码:

此时需要将构造函数设为私有,只能调用GetInstance来获得类。

首先我们需要定义一个静态的线程池变量以及静态的线程池方法,我们使用懒汉模式来实现。

当一个线程进入GetInstance函数时,要创建变量,但是被切走了,此时其他线程进入,就会导致线程安全的问题,因此需要进行加锁的操作。

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>
#include "task.hpp"
using namespace lf_task;

namespace lf_threadpool
{
    template <class T>
    class ThreadPool
    {
    private:
        ThreadPool(int num = 5)
            : _num(num)
        {
            pthread_mutex_init(&mtx, nullptr);
            pthread_cond_init(&cond, nullptr);
        }
        ThreadPool(const ThreadPool<T>& tp)=delete;
        ThreadPool<T>& operator=(ThreadPool<T>& tp)=delete;
        static ThreadPool<T>* ins;
    public:
        static ThreadPool<T>* get_instance()
        {
            static pthread_mutex_t Lock=PTHREAD_MUTEX_INITIALIZER;
                pthread_mutex_lock(&Lock);
                if(ins==nullptr)
                {
                    ins=new ThreadPool<T>();
                    ins->InitThreadPool();
                }
                pthread_mutex_unlock(&Lock);
                return ins;
        }
        bool IsEmpty()
        {
            return _task_queue.empty();
        }
        void lock()
        {
            pthread_mutex_lock(&mtx);
        }
        void unlock()
        {
            pthread_mutex_unlock(&mtx);
        }
        void Wait()
        {
            pthread_cond_wait(&cond, &mtx);
        }
        void Wakeup()
        {
            pthread_cond_signal(&cond);
        }
        void PushTask(const T &in)
        {
            pthread_mutex_lock(&mtx);
            _task_queue.push(in);
            pthread_mutex_unlock(&mtx);
            pthread_cond_signal(&cond);
        }
        void PopTask(T *out)
        {
            *out = _task_queue.front();
            _task_queue.pop();
        }
        static void *Rountine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadPool<T> *tp = (ThreadPool<T> *)args;
            while (true)
            {

                tp->lock();
                while (tp->IsEmpty())
                {
                    tp->Wait();
                }
                T t;
                tp->PopTask(&t);
                tp->unlock();
                t.Run();
            }
        }
        void InitThreadPool()
        {
            pthread_t tid;
            for (int i = 0; i < _num; i++)
            {
                pthread_create(&tid, nullptr, Rountine, (void *)this);
            }
        }
        ~ThreadPool()
        {
            pthread_mutex_destroy(&mtx);
            pthread_cond_destroy(&cond);
        }

    private:
        int _num;
        queue<T> _task_queue;
        pthread_mutex_t mtx;
        pthread_cond_t cond;
    };
    template<class T>
    ThreadPool<T>* ThreadPool<T>::ins=nullptr;
}

同时主函数调用静态方法创建线程池:

#include"thread_pool.hpp"
using namespace ns_threadpool;
int main()
{
    srand((long long)time(nullptr));
    while(true)
    {
        int x=rand()%20+1;
        int y=rand()%10+1;
        char arr[]="+-*/%";
        char op=arr[rand()%5];
        Task t(x,y,op);
        ThreadPool<Task>::get_instance()->PushTask(t);
        sleep(1);
    }
}


http://www.kler.cn/a/400288.html

相关文章:

  • PhpSpreadsheet导出图片
  • delphi fmx android 离线人脸识别
  • LeetCode 2816.翻倍以链表形式表示的数字
  • ISCTF 2024 web
  • 基于Canny边缘检测和轮廓检测
  • 学习日记_20241117_聚类方法(高斯混合模型)
  • Java项目实战II基于Java+Spring Boot+MySQL的共享汽车管理系统(源码+数据库+文档)
  • uniapp 设置form表单以及验证密码
  • SQL,力扣题目1369,获取最近第二次的活动
  • uniapp Uview上传图片组件Upload会自动刷新
  • mysql的my.cnf配置文件参数说明
  • go语言中的占位符有哪些
  • 性能调优专题(12)之垃圾收集器ParNewCMS与底层三色标记算法详解
  • idea 通过git撤销commit但未push的操作
  • 没钱买KEGG怎么办?REACTOME开源通路更强大
  • 4K双模MiniLED显示器哪个好
  • 大数据平台的网络安全架构
  • 学习笔记024——Ubuntu 安装 Redis遇到相关问题
  • 【网络安全】SSL(二):Keyless SSL技术细节
  • 电子电气架构 --- 车载通信框架和对应技术
  • 使用MaxKB搭建知识库问答系统并接入个人网站(halo)
  • Node.js 和 Express 搭建一个简单的 Web 应用程序
  • 运维面试题.云计算面试题集锦之二
  • List、ArrayList与顺序表1
  • Windows安装vcpkg教程(VS2022)
  • 第二十一章 TCP 客户端 服务器通信 - 客户端OPEN命令