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


    一、内存池

    在这里插入图片描述

    当用户调用new等函数的时候,会向内核申请空间。每调用一次申请一次空间而且在空间不够的时候还会执行内存算法等,需要花费时间。因此在程序创建之初,OS索性直接将一大块内存分配给用户。至于什么时候释放,怎么用都由用户自己决定。这就是内存池的概念。
    内存池的目的是为了提高效率,线程池也同理。
    因此我们可以类比出线程池的概念,即提前创建一批线程,以便于随时处理任务。

    二、线程池的实现

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

    1.任务文件

    #include
    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;
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    2.主函数

    #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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.线程池

    #include
    #include
    #include
    #include
    #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);
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    三、单例模式

    单例模式即只让对象在内存中存在一份,即一个类只定义一个对象,对于线程池来说只有一个线程池就够了。因此线程池的定义可以使用单例模式。
    一般而言,需要采用单例模式的情景是:

    1.语义上只需要一个对象。
    2.该对象内部存在大量空间保存大量数据,若存在多份(或各种拷贝),内存中就存冗余数据。

    1.懒汉式

    template<class T>
    class Singleton
    {
    	static T* inst;
    public:
    	static T* GetInstance()
    	{
    		if(inst==NULL)
    		{
    			inst=new T();
    		}
    		return inst;
    	}	
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    2.饿汉式

    template<class T>
    class Singleton
    {
    	static T data;
    public:
    	static T* GetInstance()
    	{
    		return &data;
    	}	
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    3.线程安全

    由于单例本身会在任何场景,任何环境下被调用。因此可能会导致Getinstance被重入而产生线程安全问题。
    我们可以使用单例模式改写线程池的代码:
    此时需要将构造函数设为私有,只能调用getinstance函数来获得类。
    首先我们需要定义一个静态的线程池变量以及静态的线程池方法,我们使用懒汉式来实现。
    当一个线程进入getinstance函数时,要创建变量,但是被切走了,此时其他线程进入,就会导致线程安全的问题,因此需要进行加锁的操作。

    #include
    #include
    #include
    #include
    #include"Task.hpp"
    using namespace ns_task;
    namespace ns_threadpool
    {
        template<class T>
        class ThreadPool
        {
            private:
            static ThreadPool<T>* ins;
            int num_;
            queue<T> task_queue_;
            pthread_mutex_t mtx;
            pthread_cond_t cond;  
            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;
            public:
            static ThreadPool<T>* get_instance()
            {
            if(ins==nullptr)
            {
                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)
            {
                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);
            }
        };
        template<class T>
        ThreadPool<T>* ThreadPool<T>::ins=nullptr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

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

    #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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    《c++并发编程实战》之第3章 线程共享
    Django路由层之有名分组和无名分组、反向解析、路由分发、伪静态的概念、名称空间、虚拟环境、Django1和Django2的区别
    C/C++ 快速入门
    【java】输入输出流
    MediaCodec同步异步使用
    leecode#Excel表列序号#组合两个表
    [代码学习]matmul的理解与使用
    23种设计模式之状态模式(State Pattern)
    Android TV开发的焦点记忆 焦点移动到列表中的某一项中,焦点移出去,在回来时焦点还要定位到原来的item上
    【Android学习】Android studio环境搭建-解决下载gradle慢&加载mainfest.xml慢的问题
  • 原文地址:https://blog.csdn.net/qq_51492202/article/details/126091658