Linux之多线程第五部分:线程池

线程池概念

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
*线程池的应用场景:
1.需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2.对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
3.接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

  • 线程池的种类:
  • 线程池示例:
    1. 创建固定数量线程池,循环从任务队列中获取任务对象,
    1. 获取到任务对象后,执行任务对象中的任务接口
      说白了就是预先创建若干个线程等待任务,省去了一个一个创建的时间
      Linux之多线程第五部分:线程池_第1张图片

模拟线程池

本质上和我们原来写过的消费者生产者模型类似,只不过把创建线程放到了类中,一次性创建多个线程,相当于模拟了内存池。
.hpp

#pragma once
#include 
using namespace std;
#include 
#include 
#include 
namespace ns_thread
{
    const int default_num = 5;
    pthread_mutex_t mtx;
    pthread_cond_t c_cnd;

    template <class T>
    class ThreadPool
    {
    private:
        //线程池中线程个数
        int _num;
        queue<T> task_queue;
    public:
        void Lock()
        {
            pthread_mutex_lock(&mtx);
        }
        void Unlock()
        {
            pthread_mutex_unlock(&mtx);
        }
        void Wait()
        {
            pthread_cond_wait(&c_cnd, &mtx);
        }
        void WakeUp()
        {
            pthread_cond_signal(&c_cnd);
        }
        bool IsEmpty()
        {
            return task_queue.empty();
        }
    public:
        ThreadPool(int num = default_num)
            : _num(num)
        {
            pthread_mutex_init(&mtx, nullptr);
            pthread_cond_init(&c_cnd, nullptr);
        }

        // 在类中要让线程执行类内成员方法,是不可行的!!因为会有隐藏this指针就不符合创建线程的格式了
        // 必须让线程执行静态方法,静态成员函数无法访问私有属性
        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->Pop(&t);
                tp->Unlock();
                //这里先释放锁再进行任务处理,能达到一个线程处理任务,另一个线程可以抢锁获取数据,达到了并行的效果。
                //如果先处理任务再释放锁,那么每个线程处理任务就是串行的,效率肯定没有并行的高
                t.Run();
            }
        }

        void InitThreadPool()
        {
            pthread_t tid;
            for (int i = 0; i < _num; i++)
            {
                pthread_create(&tid, nullptr, Rountine, (void *)this);
            }
        }

        void Push(const T &in)
        {
            Lock();
            task_queue.push(in);
            Unlock();
            //唤醒线程处理任务
            WakeUp();
        }

        void Pop(T *out)
        {
            *out = task_queue.front();
            task_queue.pop();
        }

        ~ThreadPool()
        {
            pthread_mutex_destroy(&mtx);
            pthread_cond_destroy(&c_cnd);
        }
    };
}

.cpp

#include
#include"thread_pool.hpp"
#include"task.hpp"
#include
using namespace std;
using namespace ns_thread;
using namespace ns_task;


int main()
{
    srand((long long)time(nullptr));
    ThreadPool<Task> *tp = new ThreadPool<Task>();
    tp->InitThreadPool();
    while(true)
    {
        Task t(rand()%20+1, rand()%10+1, "+-*/%"[rand()%5]);
        
        tp->Push(t);
        sleep(1);
    }
    return 0;
}

task.hpp

#pragma once
#include
using namespace std;
#include
//实现+-*/%
namespace ns_task
{
    class Task
    {
    private:
        int _x;
        int _y;
        char _c;

    public:
        Task(){};
        Task(int x, int y, char c)
            : _x(x), _y(y), _c(c)
        {
        }
        ~Task(){};

        void Run()
        {
            int res = 0;
            switch (_c)
            {
            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<<_c<<_y<<" = "<<res<<endl;
        }
    };
}

具体逻辑为:
先提前创建一批线程,然后这些线程挂起等待
当接受任务时,唤醒在等待队列中的的线程,处理任务 。这里代码的细节要单拿出来强调一下:
Linux之多线程第五部分:线程池_第2张图片
Linux之多线程第五部分:线程池_第3张图片

你可能感兴趣的:(linux,c++)