【Linux】Linux 线程池基础

参考博客:https://blog.csdn.net/sjsjnsjnn/article/details/126364511

一、什么是线程池

  • 线程池是线程的一种使用模式。在前面的情况中,我们都是遇到任务然后创建线程再执行。但是线程的频繁创建就类似于内存的频繁申请,会给操作系统带来更大的压力,进而影响整体的性能。

  • 所以我们一次申请好一定数量而定线程,然后将线程的管理操作交给线程池,就避免了在短时间内不断创建与销毁线程的代价,线程池不但能够保证内核的充分利用,还能防止过分调度,并根据实际业务情况进行修改。

二、线程池的优点

  • 任务来到立马就有线程去执行任务,节省了创建线程的时间。
  • 防止服务器线程过多导致的系统过载问题
  • 相对于进程池,线程池资源占用较少,但是健壮性很差

三、线程池的应用

需要大量的线程来完成任务,且完成任务的时间比较短

  • 例如:WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

对性能要求苛刻的应用

  • 比如要求服务器迅速响应客户请求。

接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用

  • 突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

四、实现一个简单的线程池

线程池中提供了一个任务队列,以及若干个线程。示意图如下:
在这里插入图片描述

我们创建好了线程池之后,首次我们先是对其进行初始化操作;然后不断的向任务队列塞数据,由线程池中的线程去获取任务并执行相关操作;

  1. 任务队列(即临界资源)是会被多个执行流同时访问,因此我们需要引入互斥锁对任务队列进行保护。

  2. 线程池中的线程想要获取到任务队列中的任务,那么就必须要确保任务队列中有任务,所以我们还需引入条件变量来进行判断,如果队列中没有任务,线程池中的线程将会被挂起,直到任务队列中有任务后才被唤醒;

  3. 在thread_pool.hpp中,多线程去执行对应的方法的时候,采用的是静态成员函数,这样做的目的是解决类中存在隐藏的this指针问题,因为多线程在调用对应的函数时,该函数只有一个形参,不加static的话,那么形参个数就有两个,是不可以的;所以我们可以将this指针作为参数传递过去,就可以访问类内的成员函数了;

下面是完整的代码实现

threadpool.hpp

  • 这个文件封装了线程池,使用模板的template的方式,创建了一个线程池
  • 初始化线程池的时候,所有的线程都设置了detach模式,这样最后不需要手动join线程,我们不关心线程池关闭之后所有线程之间的情况
  • 任务对象需要有Run的接口
#pragma once
#include<iostream>
#include<string>
#include<queue>
#include<unistd.h>
#include<pthread.h>

template<typename T>
class ThreadPool
{
public:
    void Lock(){ pthread_mutex_lock(&mutex); }
    void Unlock(){ pthread_mutex_unlock(&mutex); }
    
    bool IsEmpty(){ return task_queue.empty();}
    
    void Wait(){ pthread_cond_wait(&cond,&mutex); }
    void WakeUp(){ pthread_cond_signal(&cond); }

    void PushTask(const T& task){  
        Lock();
        task_queue.push(task);
        Unlock();
        WakeUp();
    }

    void PopTask(T* outTask){
        *outTask = task_queue.front();
        task_queue.pop();
    }



public:
    ThreadPool(int num_ = 8):num(num_)
    {
       pthread_mutex_init(&mutex,NULL);
       pthread_cond_init(&cond,NULL);     
    }

    static void* Routine(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(); //自定义的Run接口
        }
    }
    
    void initThreadPool(){
        pthread_t tid;
        for(int i = 0 ; i < num ; ++i){
            pthread_create(&tid,NULL,Routine,(void*)this);
        }
    }

    ~ThreadPool(){
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);
    }



private:
    int num; //线程数量
    std::queue<T>task_queue;
    pthread_mutex_t mutex;
    pthread_cond_t cond;

};

task.hpp

  • 这个文件封装的一个任务对象,有Run接口,用于适配线程池
  • 我们使用数学计算来模拟任务,实际情况可能复杂的多
#pragma once
#include <iostream>
#include <pthread.h>

class Task
{

public:
    Task() {}
    Task(int x_, int y_, char op_) : x(x_), y(y_), op(op_) {}

    int Run()
    {
        int ret = 0;
        switch (op)
        {
        case '+':
            ret = x + y;
            break;
        case '-':
            ret = x - y;
            break;
        case '*':
            ret = x * y;
            break;
        case '/':
            ret = x / y;
            break;
        default:
            break;
        }

        std::cout << "thread id = " << pthread_self() << ",result:" << x << op << y << "=" << ret << std::endl;
        return ret;
    }

    int operator()(){ return Run(); }

    ~Task(){}

private:
    int x;
    int y;
    char op;
};

main.cpp

  • main函数中使用这个线程池,并且持续加入任务
#include"threadpool.hpp"
#include"task.hpp"

char operations[4] = {'+','-','*','/'};
int main(){
    ThreadPool<Task>* threadPool = new ThreadPool<Task>(8);
    threadPool->initThreadPool();

    srand(time(NULL));
    while(true){
        int x = rand() % 100;
        int y = rand() % 100;
        char op = operations[rand() % 4];

        Task task(x,y,op);
        threadPool->PushTask(task);
        sleep(1);
    }


    return 0;
}

Makefile

  • 编写Makefile编译项目
CXX = g++

CXXFLAGS = -Wall -std=c++14

SRCS = main.cpp 

OBJS = $(SRCS:.cpp=.o)

TARGET = threadPool

all:$(TARGET)

$(TARGET):$(OBJS)
	$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS)
	
%.o:%.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@
		
clean:
	rm -f $(OBJS) $(TARGET)
	
.PHONY:all clean	

运行结果

  • 可以发现,持续有线程从任务队列中获取任务并执行
    在这里插入图片描述

更多资料:https://github.com/0voice

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值