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

目录

一、内存池

二、线程池的实现

1.线程池

2.任务文件

3.主函数

三、单例模式

1.懒汉模式

2.饿汉模式

3.线程安全


一、内存池

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

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

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

二、线程池的实现

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

1.任务文件

cpp 复制代码
#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.线程池

cpp 复制代码
#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.主函数

cpp 复制代码
#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.懒汉模式

cpp 复制代码
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.饿汉模式

cpp 复制代码
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函数时,要创建变量,但是被切走了,此时其他线程进入,就会导致线程安全的问题,因此需要进行加锁的操作。

cpp 复制代码
#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;
}

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

cpp 复制代码
#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);
    }
}
相关推荐
马剑威(威哥爱编程)2 天前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
gjh12082 天前
单例模式和适配器模式的简单介绍
单例模式·适配器模式
无尽的大道3 天前
单例模式详解:如何优雅地实现线程安全的单例
单例模式
Hello.Reader3 天前
单例模式全面解析
单例模式
编程修仙3 天前
java的单例设计模式
java·单例模式·设计模式
L_cl4 天前
Python学习从0到1 day27 Python 高阶技巧 ③ 设计模式 — 单例模式
学习·单例模式·设计模式
ktkiko116 天前
Java中的设计模式——单例模式、代理模式、适配器模式
java·单例模式·设计模式
傻傻虎虎6 天前
【真题笔记】21年系统架构设计师案例理论点总结
单例模式·系统架构·uml·命令模式
Mr. zhihao7 天前
享元模式及其运用场景:结合工厂模式和单例模式优化内存使用
单例模式·享元模式