自己写Http服务器(四)添加线程池

在我们服务器之前的那部分,我们通过创建线程让线程去处理任务,从而可以在同一时间可以处理多个请求,但是这样则需要我们频繁的创建、销毁线程,这样在一定程度上会产生资源的损耗,影响我们服务器的效率,其次如果短时间内的大量请求,导致服务器创建线程数量过多,可能导致内存达到极限,影响操作系统中其他重要进程的运行

针对我们目前的服务器可以支持常规的短链接web http请求,因为单个任务比较小,为了减小在创建销毁线程这个过程中所带来的资源消耗,我们可以采用线程池相关的技术来代替创建线程处理请求

 

关于线程池:

  • 在面向对象的编程中,创建和销毁对象是很消耗时间和资源的,所以提高服务程序效率的一个手段就是尽可能的减少创建和销毁对象的次数

  • 那么如何利用已有资源来服务就是一个需要解决的关键问题,其实这也就是一些“池化技术”产生的原因

  • 我们可以通过重复利用已有资源来有效的提高效率,线程池也是在这一思想下的产物

  • 我们可以创建一个固定数量线程的一个线程池,它包括固定数量的线程,以及附带一个任务队列用来存放用户需要处理的任务,当用户有新的任务,只需要将任务添加到任务队列中,如果有空闲的线程,那么就去队列中拿取任务执行,如果当前各个线程都处于忙碌状态 ,那么就等待,直至某个线程空闲,就会来任务队列中取任务

  • 基于以上思想,我们来写一个线程池,让他可以用来处理我们服务器的请求,提高我们服务器的效率

 

首先我们先创建一个Task类,用来存放我们需要处理的任务信息

  • 针对我们的服务器我们在这个Task类中我们定义了两个私有成员int _sock和Handler _handler分别表示我们当前任务的sock文件描述符以及如何处理的入口函数

  • 提供了两个方法SetTask和Run分别用来设置Task成员的值以及运行处理函数进行处理

    typedef void* (*Handler)(int);
    
    class Task
    {
    private:
        int _sock;
        Handler _handler;
    public:
        Task()
        {
            _sock = -1;
            _handler = NULL;
        }
        void SetTask(int sock, Handler handler)
        {
            _sock = sock;
            _handler = handler;
        }
        void Run()
        {
            _handler(_sock);
        }
        ~Task()
        {}
    };

    接着我们来看ThreadPool类

  • 我们定义私有成员 _thread_total_num来记录线程总数量,_thread_idle_num记录当前休眠中的线程数量,_task_queue用来存放任务的任务队列,_lock互斥锁用来对线程的互斥访问,_cond条件变量

  • Init函数实现对线程池的初始化,创建_thread_total_num个线程并且让他们执行ThreadRoutine函数

  • 在ThreadRoutine函数内部先对线程进行线程分离,然后里面while循环,处理任务,获取锁,判断队列是否为空,拿Task处理,否则休眠

  • 每次当往队列中push Task时候要唤醒休眠中的线程

#define NUM 5

class ThreadPool
{
private:
    int _thread_total_num;
    int _thread_idle_num;
    std::queue _task_queue;
    pthread_mutex_t _lock;
    pthread_cond_t _cond;

public:
    void LockQueue()
    {
        pthread_mutex_lock(&_lock);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_lock);
    }
    
    bool IsEmpty()
    {
        return _task_queue.empty();
    }
    
    void ThreadIdle()
    {
        _thread_idle_num++;
        pthread_cond_wait(&_cond, &_lock);
        _thread_idle_num--;
    }
    
    void WeakOneThread()
    {
        pthread_cond_signal(&_cond);
    }
    
    static void* ThreadRoutine(void* arg)
    {
        ThreadPool* tp = (ThreadPool*)arg;
        pthread_detach(pthread_self());
        
        while(1)
        {
            tp->LockQueue();
            while(tp->IsEmpty())
            {
                tp->ThreadIdle();
            }
            Task t;
            tp->PopTask(t);
            tp->UnlockQueue();
            LOG(INFO,"Task has be taken, handler...");
            t.Run();
        }
    }

    ThreadPool(int num = NUM)
        :_thread_total_num(num),_thread_idle_num(0)
    {
        pthread_mutex_init(&_lock, NULL);
        pthread_cond_init(&_cond, NULL);
    }
    
    void InitThreadPool()
    {
        for(int i = 0; i < _thread_total_num; ++i)
        {
            pthread_t tid;
            pthread_create(&tid, NULL, ThreadRoutine, this);
        }
    }
    
    void PushTask(Task &t)
    {
        LockQueue();
        _task_queue.push(t);
        WeakOneThread();
        UnlockQueue();
    }
    
    void PopTask(Task &t)
    {
        t = _task_queue.front();
        _task_queue.pop();
    }
    
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }
};

基本上线程池这部分就这么些内容,在我们之前原来Http服务器的基础上往往HttpdServer类的基础上加上线程池即可

你可能感兴趣的:(HttpdServer)