【Linux】:线程池(逐行解析代码)

发布时间:2024年01月23日

一.概念

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

在这里插入图片描述

线程池的应用场景:

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

线程池示例:

  1. 创建固定数量线程池,循环从任务队列中获取任务对象,
  2. 获取到任务对象后,执行任务对象中的任务接口

二.模拟实现一个线程池

1.我们要写一个线程池类(ThreadPool),首先要写它的构造和析构,很明显是多个线程,那么需要线程的个数(num),同时保证它们的互斥关系,需要锁(mutex)和条件变量(cond)。

#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>

//每个线程的基本信息
struct ThreadInfo
{
  pthread_t tid;
  std::string name;
};

//。默认线程池里有5个线程
static const int defalutnum = 5;

template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
public:
  ThreadPool(int num=defalutnum): threads_(num)
  {
    pthread_mutex_init(&mutex,nullptr);//初始化锁
    pthread_cond_init(&cond,nullptr);//初始化条件变量
  }

  ~ThreadPool()
  {
    pthread_mutex_destroy(&mutex);//销毁锁
    pthread_cond_destroy(&cond);//销毁条件变量
  }

private:
  std::vector<ThreadInfo> threads_;///线程池
  std::queue<T> tasks_;//任务

  pthread_mutex_t mutex;//锁
  pthread_cond_t cond;//条件变量
};

2.开始创建线程,给外部留一个Start接口,方便进行使用。

在这里插入图片描述

但这里有一个问题:当编译时会发生报错,因为HandlerTask属于ThreadPool的内置函数,所以第一个参数是隐藏的this指针,而线程函数的格式要求参数有且只有一个是void*类型。所以要么将它放在类外,要么就加static。

在这里插入图片描述

3.写一个Push函数用来添加任务。同时对上锁,解锁,休眠…进行封装以方便使用。

#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>

//每个线程的基本信息
struct ThreadInfo
{
  pthread_t tid;
  std::string name;
};

//。默认线程池里有5个线程
static const int defalutnum = 5;

template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
private:
  void Lock()//上锁
  {
    pthread_mutex_lock(&mutex);
  }
  void Unlock()//解锁
  {
    pthread_mutex_unlock(&mutex);
  }
  void Wakeup()//唤醒
  {
    pthread_cond_signal(&cond);
  }
  void Threadsleep()//休眠
  {
    pthread_cond_wait(&cond,&mutex);
  }
public:
  ThreadPool(int num=defalutnum): threads_(num)
  {
    pthread_mutex_init(&mutex,nullptr);//初始化锁
    pthread_cond_init(&cond,nullptr);//初始化条件变量
  }

  static void* HandlerTask(void* arges)
  {

  }
  void Start()
  {
    int num=threads_.size();
    for(int i=0;i<num;i++)
    {
      threads_[i].name="thread-"+std::to_string(i);//把线程名写入
      pthread_create(&(threads_[i].tid),nullptr,HandlerTask,nullptr);//创建线程
    }
  }
  void Push(const T&t)//向任务表里添加任务
  {
    //保证互斥要上锁
    Lock();
    tasks_.push(t);
    Wakeup();//唤醒一个线程执行任务
    Unlock();
  }

  ~ThreadPool()
  {
    pthread_mutex_destroy(&mutex);//销毁锁
    pthread_cond_destroy(&cond);//销毁条件变量
  }

private:
  std::vector<ThreadInfo> threads_;//线程池
  std::queue<T> tasks_;//任务

  pthread_mutex_t mutex;//锁
  pthread_cond_t cond;//条件变量
};

4.接下来就是多个线程如何执行任务了。

在这里插入图片描述

问题:这里编译会直接报错,因为HandlerTask是静态函数,不能直接使用成员变量。解决方法:传this指针。同时前面的方法是私有的,也需要改成公有的。

#include<iostream>
#include<pthread.h>
#include<vector>
#include<string>
#include<queue>

//每个线程的基本信息
struct ThreadInfo
{
  pthread_t tid;
  std::string name;
};

//。默认线程池里有5个线程
static const int defalutnum = 5;

template<class T>//由于不知道任务类型,用T代替
class ThreadPool
{
public:
  void Lock()//上锁
  {
    pthread_mutex_lock(&mutex);
  }
  void Unlock()//解锁
  {
    pthread_mutex_unlock(&mutex);
  }
  void Wakeup()//唤醒
  {
    pthread_cond_signal(&cond);
  }
  void Threadsleep()//休眠
  {
    pthread_cond_wait(&cond,&mutex);
  }
  bool Isempty()
  {
    return tasks_.empty();
  }
public:
  ThreadPool(int num=defalutnum): threads_(num)
  {
    pthread_mutex_init(&mutex,nullptr);//初始化锁
    pthread_cond_init(&cond,nullptr);//初始化条件变量
  }

  static void* HandlerTask(void* arges)
  {
    ThreadPool<T>* tp=static_cast<ThreadPool<T>*>args;
    while(true)
    {
      //先上锁
      tp->Lock();
      while(tp->Isempty())//使用while防止误唤醒
      {
        Threadsleep();//如果没有任务先休眠
      }
      T t=tp->Pop();//封装一个Pop函数
      tp->Unlock();//解锁

      //执行任务

      //t();//这里我写的样例任务是重载了(),所以直接用括号表示执行任务(大家可以自己任意写任务)
    }
  }
  T Pop()
  {
    T t=tasks_.front();
    tasks_.pop();
    return t;
  }
  void Start()
  {
    int num=threads_.size();
    for(int i=0;i<num;i++)
    {
      threads_[i].name="thread-"+std::to_string(i);//把线程名写入
      pthread_create(&(threads_[i].tid),nullptr,HandlerTask,this);//创建线程
    }
  }
  void Push(const T&t)//向任务表里添加任务
  {
    //保证互斥要上锁
    Lock();
    tasks_.push(t);
    Wakeup();//唤醒一个线程执行任务
    Unlock();
  }

  ~ThreadPool()
  {
    pthread_mutex_destroy(&mutex);//销毁锁
    pthread_cond_destroy(&cond);//销毁条件变量
  }

private:
  std::vector<ThreadInfo> threads_;//线程池
  std::queue<T> tasks_;//任务

  pthread_mutex_t mutex;//锁
  pthread_cond_t cond;//条件变量
};

整个线程池已经简单的写完了,大家可以自己写一些任务来进行测试啦。

文章来源:https://blog.csdn.net/m0_73790767/article/details/135768888
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。