线程池

原理

在这里插入图片描述

如我们每次动态开辟一个变量空间的时候,都会有效率的损失,如果有一个内存池,需要的化直接在里面取就行了,这就大大的提高了效率

当我们创建任务的时候再去申请线程,就相当于我们需要malloc的时候再去申请空间,创建线程也是有成本的,

请求来了,线程要提前准备好,任务来了,就指派给他
提前准备好的线程,原来随时处理任务就叫做线程池
(提高效率)

实现

Task.hpp

使用这个Task类,将此作为里面的类型

Task.hpp

#pragma once
#include <iostream>
#include <pthread.h>

namespace ns_task
//使用命名空间,提高代码的安全性
{
    class Task
    {
    private:
        int _x;
        int _y;
        char _op; //表示+-*/%
    public:
        Task() //无参构造,为了拿任务,不需要参数列表
        {
        }
        //进行函数重载
        Task(int x, int y, char op) //有参构造,制造任务
            : _x(x), _y(y), _op(op)
        {        }
        ~Task()//析构函数其实也不需要我们来实现,因为没有变量需要销毁
        {
        }
        int Run()//task类,处理任务
        {
            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:
                std::cout << "bug?" << std::endl;
                break;
            }
            std::cout << "当前任务正在被:" << pthread_self() << "处理:" << _x << _op << _y << "=" << res << std::endl;
            return res;
        }
        Task operator=(Task &s)//赋值运算符重载
        {
            if (this != &s)
            {
                _x = s._x;
                _y = s._y;
                _op = s._op;
            }
            return *this;
        }
        
        //Task t;
        //t()//
        int operator()()//将()重载,仿函数
        {
            return Run();//()使用run()
        }
    };
}

threadpool.hpp

实现线程池类,高封装性,将所有的对于线程的操作都放在这个类里面是实现

#pragma once
#include <iostream>
#include <string>
#include <queue>
#include<unistd.h>
#include<pthread.h>
// #cludine"Task.hpp"
// using namespace ns_task;
namespace ns_threadpool
{
    const int g_num=5;//使用一个全局变量,作为我们需要创建的线程的个数
    template <class T>//使用模板,可以实现泛型编程,多个类型都可以使用
    class ThreadPool //线程池
    {

    private:
        int num_;                  //一个线程池里面有多少个任务
        std::queue<T> task_queue_; //任务队列,临界资源
        pthread_mutex_t mtx_;//锁
        pthread_cond_t cond_;//条件变量

    public:
        ThreadPool(int num=g_num) :num_(num)//初始化构造函数
        {
            pthread_mutex_init(&mtx_,nullptr);
            pthread_cond_init(&cond_,nullptr);
        }
        
        
        ~ThreadPool()
        {
            pthread_mutex_destroy(&mtx_);
            pthread_cond_destroy(&cond_);
        }

        //在类中,要让

        static void* Rountine(void* args)
        //主线程里面往里面塞任务,而线程里面主要就是在里面处理任务
        //也不能访问类里面非static成员
        {

            pthread_detach(pthread_self());//实现线程分离就不要再去join等待了
            ThreadPool<T>* tp=(ThreadPool<T>*)args;//类型转换
            while(true)
            {
                //从任务队列里面去拿一个任务
                //执行任务,要先把这个任务队列锁主

                //每个线程他跟放任务的线程一样,都是竞争式的去拿一个任务
                tp->Lock();
                //先检测任务队列是否有一个任务
                while(tp->IsEmpty())
                {
                    //检测到任务队列为空
                    //此时线程就挂起等待
                    tp->Wait();
                }
                //该任务队列里面一定有任务了
                T t;
                tp->PopTask(&t);
                //任务就拿到了
                tp->UnLock();
                t.Run();//可能有多个线程在处理任务,

                sleep(1);
            }
        }

		//我们定义了一个线程池的变量,就需要首先先进行初始化
        void InitThreadPool()
        {
            //初始化一批线程,
            //这样就不要每次用都要去开辟线程了
            pthread_t tid;//一次创建一批线程
            for(int i=0;i<num_;i++)
            {
                pthread_create(&tid,nullptr,Rountine,(void*)this);//在create里面实现函数,所以我们需要在类里面实现这个函数
                //在类中不能执行线程的方法,因为他都有隐藏的this指针
                //所以我们需要使用静态的函数,就没有了this指针
            }
        }
        
        void PopTask(T* out)//头删,在线程池里面取出任务
        {
            *out=task_queue_.front();
            task_queue_.pop();
        }
        void Wait()
        {
            pthread_cond_wait(&cond_,&mtx_);
        }
        bool IsEmpty()
        {
            return task_queue_.empty();
        }
        void Lock()
        {
            pthread_mutex_lock(&mtx_);
        }
        void UnLock()
        {
            pthread_mutex_unlock(&mtx_);
        }
        void Wakeup()
        {
            pthread_cond_signal(&cond_);
        }
        
		//我们定义了一个变量之后,初始化之后,就要向里面放任务,然后里面的线程,通过里面的队列,争抢任务
		
        void PushTask(const T & in)
        {
            //塞任务,就相当于一个生产者,生产者之间要进行互斥访问
            Lock();
            task_queue_.push(in);
            UnLock();
            Wakeup();//塞完任务之后,就要唤醒里面的队列,来取任务
        }

        //万一任务队列里面一个任务都没有的话,那么线程池里面的每一个线程就要处于休眠状态,挂起等待
        
    };
}

main.cc

非单例模式


#include"threadpool.hpp"
#include"Task.hpp"
#include<unistd.h>
#include<cstdlib>
#include<ctime>
using namespace ns_task;//使用我们写的两个命名空间,提高了代码的封装性
using namespace ns_threadpool;

//线程池就是一个单例模式,只有一个线程池就够了

int main()
{
    ThreadPool<Task>* tp=new ThreadPool<Task>();//我们一开始就创建一个线程池类型,线程池类型我们只需要一个就行了,
    tp->InitThreadPool();
    //我们希望是主线程不断的向线程池里面push任务,线程池里面竞争任务,处理这些任务
    //外部可能存在一个或者多个线程向里面塞任务
    srand((long long)time(nullptr));
    while(true)
    {
        //以后就是从网络里面来
        //主线程就是把任务放到线程池里面去
        
        //有的时候访问网站,挂掉了,OS受不了了,杀掉

        Task t(rand()%20+1,rand()%10+1,"+-*/%"[rand()%5]);
        tp->PushTask(t);//放进去让他进行处理工作
        

        
    }

    return 0;
}

详细代码可以查看
ThreadPool

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevin~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值