【Linux】线程池知识点复习

线程池

​ 线程池的实现 = 线程安全的队列 + 很多线程、

为何要有线程池?

在实际的业务场景中,一台服务器可能在同一时刻涌入大量的请求,但是这个请求五花八门,在程序不崩溃的情况下,需要竟可能多的处理请求。
如果只有一个线程,程序效率低

如果在请求到来的时候创建线程会有大量的时间成本

如果没有开辟线程数量的限制,有可能会耗尽CPU的资源导致程序崩溃

这就产生了线程池的需要,根据系统资源的多少实时维护一定数量的线程,根据请求数量的多少分配线程,把传过来的请求放到线程安全队列中去。

如何让入口函数相同的线程去处理不同的请求?

向线程池传入数据的时候,同时一起传入处理该数据的方法(函数入口地址),线程池当中的线程只需要调用传入的函数处理数据即可。(线程安全的队列中的元素:数据 + 处理函数的地址)

如何实现线程池?

由于线程安全队列当中节点的数据类型是自定义类型

封装一个任务类(请求数据+处理函数)
封装线程池(线程安全的队列–>STL queue ,互斥锁,条件变量,一大堆线程)

#include 
#include 
#include 
#include 
#include 

#define THREADCOUNT 4
//封装任务类

void ThreadTaskFunc(int data)
{
    data += 10;
    printf("i recv Data [%d]\n",data);
}

typedef void (*Handler_t)(int)
class ThreadTask
{
public:
    ThreadTask()
    {
        Data_ = -1;
        Handler = NULL;
    }
    ThreadTask(int Data,Handler_t handler)
    {
        Data_ = Data;
        Handler = handler;
    }
    //提供外部调用的接口
    void run()
    {
        Handler(Data_);
    }
private:
    int Data_;
	Handler_t Handler;    
};

class ThreadPool
{
public:
    ThreadPool()
	{
    	Capacity_ = 10;
    	ThreadCapacity_ = THREADCOUNT;
   		ThreadCurNum = THREADCOUNT;
    	pthread_mutex_init(&Mutex_,NULL);
    	pthread_cond_init(&Cond_,NULL);
    
    	IsExit = false;
    
    	//创建线程
    	bool IsCreate = ThreadCreate();
    	if(!isCreate)
        {
            printf("ThreadPool Create thread failed\n");
            exit(1);
        }
	}
    
    ~ThreadPool()
    {
        pthread_mutex_destory(&Mutex_);
    	pthread_cond_destory(&Cond_);
    }
    
    bool Push(ThreadTask* Tt)
    {
        pthread_mutex_lock(&Mutex_);
        //如果需要执行线程退出,不再往队列中push数据
        if(IsExit)
        {
            pthread_mutex_unlock(&Mutex_);
            return false;
        }
        Que_.push(Tt);
        pthread_mutex_unlock(&Mutex_);
        //传入请求就去通知消费线程
        pthread_cond_signal(&Cond_);
        return true;
    }
    
    bool Pop(ThreadTask** Tt)
    {
        *Tt = Que_.front();
        Que_.pop();
        return true;
    }

    void ThreadPoolClear()
    {
        pthread_mutex_lock(&Mutex_);
        IsExit = true;
        pthread_mutex_unlock(&Mutex);
        if(ThreadCurNum_ > 0)
        {
            pthread_cond_broadcast(&Cond);
        }
    }
    
    void ThreadExit()
    {
        for(int i=0;iMutex_);
            //检查队列是否为空,为空则消费线程等待
            while(tp->Que_.empty())
            {
                if(tp->IsExit)
                {
                    //线程的安全退出
                    tp->ThreadQuit();
                }
                pthread_cond_wait(&tp->Cond,&tp->Mutex_);
            }
            ThreadTask* temp;
            tp->Pop(&temp);
            pthread_mutex_unlock(&tp->Mutex_);
            temp->Run();
            //防止push中开辟的内存泄漏
            delete temp; 
        }
        //出队处理数据的操作
        return NULL;
    }
    
    bool ThreadCreate()
    {
        for(int i=0;i Que_;
    size_t Capacity_;
    
    //线程池中线程初始化的数量
    size_t ThreadCapacity_;
    
    pthread_t tid_[THREADCOUNT];
    //当前线程池中还存在多少个线程
    size_t ThreadCurNum_;

    //线程安全机制
    pthread_mutex_t Mutex_;
    //消费条件变量(只负责消耗请求)
    pthread_cond_t Cond_;
    
    //线程安全退出的标志
    bool IsExit;
};

int main()
{
    ThreadPool* tp = new ThreadPool();
    for(int i=0;i<10;i++)
    {
        ThreadTask* temp =  new ThreadTask(i,ThreadTaskFunc);
        tp->Push(temp);
    }
    tp->ThreadPoolClear();
   // tp->ThreadJoin();
    while(1)
    {
        sleep(1);
    }
    delete tp;
    return 0;
}

线程如何安全的退出?

线程目前在做的事情:

ThreadStart函数

1.加入互斥锁逻辑

2.PCB等待队列当中

3.从线程安全队列中取出数据,处理数据

(线程刚刚拿到互斥锁,就cancel了,导致后面的线程拿不到互斥锁,程序阻塞等待)

(线程退出的时候,线程池的队列中仍然有数据存在,但是线程并没有处理完成,导致业务处理不正常)

4.因此需要保证线程退出时没有正在处理的业务,不会导致由于暴力退出而影响到业务。

设计模式

一部分大佬将自己的编程经验,通过一些常见的问题或者常见的常见的场景,给出一种解决方案或者套路,让后续的程序员在遇到相似问题后,可以快速设计自己的代码。

单例模式

1.特点:提供了一个唯一的类的实例,具有全局变量的特点,在任何位置都可以通过单例类提供的获取类实例的方法来获取唯一的实例。

2.使用场景

数据池:用来缓存大量数据的数据结构,需要在不同的线程中进行读取或写入

内存池:为了程序的运行效率,将自己申请的内存管理起来,使用完成不还给操作系统

3.基础要点

  • 全局只有一个实例

  • static特性

  • 禁止自己的构造,拷贝构造

  • 线程安全

  • 用户通过接口获取实例,使用static修饰类的成员函数

4.饿汉模式(吃完饭就洗碗,此次吃饭就能直接吃)

程序在初始化的时候进行实例化,资源在程序初始化的时候就会全部加载,不牵扯线程安全。

优点:运行起来比较流畅

缺点:初始化耗时长

5.懒汉模式(吃完饭不洗碗,此次准备吃饭再去洗碗)

资源在使用的时候,才进行加载,换句话说就是,只要在对象使用的时候,才进行实例化,存在线程不安全的风险。

优点:运行没有饿汉流畅

缺点:初始化快

你可能感兴趣的:(linux)