Linux多线程学习(5)--C++实现一个线程池

版权声明:本文为博主原创文章,欢迎转载,转载请声明出处! https://blog.csdn.net/hansionz/article/details/84843750

多线程学习总结(1):https://blog.csdn.net/hansionz/article/details/84665815
多线程学习总结(2):https://blog.csdn.net/hansionz/article/details/84675536
多线程学习总结(3):https://blog.csdn.net/hansionz/article/details/84766601
多线程学习总结(4):https://blog.csdn.net/hansionz/article/details/84842790

C++实现一个简单线程池

1.什么是线程池

线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性整体性能。而线程池维护着多个线程, 等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的 量。

线程池,简单来说就是存在一堆已经创建好的线程(最大数目一定),初始时他们都处于空闲状态,当有新的任务进来,从线程池中取出一个空闲的线程处理任务,然后当任务处理完成之后,该线程被重新放回到线程池中,供其他的任务使用,当线程池中的线程都在处理任务时,就没有空闲线程供使用,此时,若有新的任务产生,只能等待线程池中有线程结束任务空闲才能执行,下面是线程池的工作原理图:

2.为什么要存在线程池

线程本身存在开销,我们利用多线程来进行任务处理,单线程也不能滥用,无止境的开新线程会给系统产生大量消耗,而线程本来就是可重用的资源,不需要每次使用时都进行初始化,因此可以采用有限的线程个数处理无限的任务

3.常见线程池应用场景

  • 需要大量的线程来完成任务,且完成任务的时间比较短。 例如WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,可以想象一个热门网站的点击次数。 但对于长时间的任务, 比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间长的多。
  • 一些对性能要求苛刻的应用,例如要求服务器迅速响应客户请求,可以利用线程池技术来减少线程创建和销毁的开销。
  • 一些接受突发性的大量请求,但不会使服务器因此产生大量线程的应用。突发性大量客户请求。在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现内存错误。

4.线程池的实现

threadpool.hpp

#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__ 

#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>

using namespace std;

//函数指针,指向任务的处理方法
typedef int (*call)(int,int);

//封装一个任务处理类
class Task 
{
public:
  Task(int x, int y, call handler)
    :_x(x)
    ,_y(y)
    ,_z(0)
    ,_handler(handler)
  {}
  //执行处理方法,保存结果
  void Run()
  {
    _z = _handler(_x,_y);
  }
  //打印结果 for test 
  void Show()
  {
    cout << "thread:" << pthread_self() << ". Result is:" << _z << endl; 
  }

private:
  int _x;
  int _y;
  int _z;//用来保存任务处理的结果
  call _handler;
};

//线程池
class ThreadPool 
{
private:
  int thread_nums;//线程个数
  queue<Task> t_queue;//任务队列
  pthread_mutex_t lock;//互斥锁,保护临界资源
  pthread_cond_t cond;//条件变量,实现同步
  bool is_stop;//标志该线程池是否要停止

private:
 
  static void *thread_routine(void *arg)
  {
    ThreadPool* tp = (ThreadPool*)arg; 
    //分离该线程,完成任务后不用join
    pthread_detach(pthread_self());
    while(1)
    {
      //使用互斥锁保护临界资源t_queue
      tp -> LockQueue();
      //使用while进行二次判断,防止假唤醒
      while(tp -> IsEmpty())
        tp -> IdleThread();

      Task t = tp -> GetTask();
      tp -> UnlockQueue();

      t.Run();
      t.Show();
    }
  }
  //唤醒条件变量等待下的一个线程
  void NotifyOneThread()
  {
    pthread_cond_signal(&cond);
  }
  //唤醒条件变量等待下的所有线程
  void NotifyAllThread()
  {
    pthread_cond_broadcast(&cond);
  }

public:
  ThreadPool(int num)
    :thread_nums(num)
    ,is_stop(false)
  {}
  //初始化线程池
  void InitThread()
  {
    //初始化条件变量和互斥锁
    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL);
    
    //创建一批线程
    for(int i = 0;i < thread_nums;i++)
    {
      pthread_t tid;
      pthread_create(&tid, NULL, thread_routine, (void*)this);
    }
  }
  //使用互斥量锁住任务队列
  void LockQueue()
  {
    pthread_mutex_lock(&lock);
  }
  //解锁
  void UnlockQueue()
  {
    pthread_mutex_unlock(&lock);
  }
  //判断任务队列是否为空
  bool IsEmpty()
  {
    return t_queue.size() == 0 ? true : false;
  }
  //使得线程在条件变量下等待
  void IdleThread()
  {
    if(is_stop)
    {
      UnlockQueue();
      thread_nums--;
      pthread_exit((void*)0);
      
      cout << "thread:" << pthread_self() << "quit!" << endl;
      return;
    }
    pthread_cond_wait(&cond, &lock);
  }
  //停止线程池
  void Stop()
  {
    LockQueue();
    is_stop = true;
    UnlockQueue();
    //线程的熟练如果大于0说明还存在线程在阻塞唤醒所有线程并停止
    if(thread_nums > 0)
    {
      NotifyAllThread();
    }
  }
  //向任务队列中添加任务
  void AddTask(Task& t)
  {
    LockQueue();
    if(is_stop)
    {
      UnlockQueue();
      return;//本线程退出
    }
    t_queue.push(t);
    NotifyOneThread();//实现同步
    UnlockQueue();
  }
  //从任务队列中拿出一个任务
  Task GetTask()
  {
    Task t = t_queue.front();
    t_queue.pop();
    return t;
  }
  //销毁条件变量和互斥量
  ~ThreadPool()
  {
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);
  }
};

#endif //__T__THREAD_POOL_H__

threadpool.cc

#include "threadpool.hpp"
//线程池中线程的熟练
const int thread_num = 5;
//向任务队列中添加几个简单任务
int Add(int x, int y)
{
  return x + y;
}
int Sub(int x, int y)
{
  return x - y;
}
int Div(int x, int y)
{
  return x/y;
}
int Mul(int x, int y)
{
  return x * y;
}

int main()
{
  ThreadPool* tp = new ThreadPool(thread_num);
  tp -> InitThread();//初始化线程池

  int count = 1;
  while(1)
  {
    sleep(1);
    Task t(count, count - 1, Add);
    tp -> AddTask(t);
    Task t1(count, count - 1,Sub);
    tp -> AddTask(t1);
    Task t2(count, count, Mul);
    tp -> AddTask(t2);
    Task t3(count, count, Div);
    tp -> AddTask(t3);
    ++count;
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hansionz/article/details/84843750