发表于 2024-11-22 11:23:53

线程池的实现与应用

一、线程池

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

1. 需要大量的线程来完成任务,且完成任务的时间比较短。WEB服务器完成网页请求如许的任务,使用线程池技术黑白常合适的。因为单个任务小,而任务数目巨大,你可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个 Telnet毗连请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2. 对性能要求苛刻的应用,比如要求服务器敏捷相应客户请求。
3. 担当突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池环境下,将产生大量线程,虽然理论上大部分操纵体系线程数目最大值不是题目,短时间内产生大量线程可能使内存到达极限,出现错误.
4.线程池示例:
1. 创建固定命量线程池,循环从任务队列中获取任务对象,
2. 获取到任务对象后,执行任务对象中的任务接口.
https://i-blog.csdnimg.cn/direct/4dc18488b5db4fd8ab4d8dbff2a13ccc.png
三、代码

主线程发布任务,多线程获得任务,执行任务
(1)任务
Task.hpp

#pragma once
#include <iostream>
#include <string>

std::string opers="+-*/%";

enum{
    DivZero=1,
    ModZero,
    Unknown
};

class Task
{
public:
    Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0)
    {
    }
    void run()
    {
      switch (oper_)
      {
      case '+':
            result_ = data1_ + data2_;
            break;
      case '-':
            result_ = data1_ - data2_;
            break;
      case '*':
            result_ = data1_ * data2_;
            break;
      case '/':
            {
                if(data2_ == 0) exitcode_ = DivZero;
                else result_ = data1_ / data2_;
            }
            break;
      case '%':
         {
                if(data2_ == 0) exitcode_ = ModZero;
                else result_ = data1_ % data2_;
            }            break;
      default:
            exitcode_ = Unknown;
            break;
      }
    }
    void operator ()()
    {
      run();
    }
    std::string GetResult()
    {
      std::string r = std::to_string(data1_);
      r += oper_;
      r += std::to_string(data2_);
      r += "=";
      r += std::to_string(result_);
      r += "[code: ";
      r += std::to_string(exitcode_);
      r += "]";

      return r;
    }
    std::string GetTask()
    {
      std::string r = std::to_string(data1_);
      r += oper_;
      r += std::to_string(data2_);
      r += "=?";
      return r;
    }
    ~Task()
    {
    }

private:
    int data1_;
    int data2_;
    char oper_;

    int result_;
    int exitcode_;
}; (2)线程池
#pragma once
#include <iostream>
#include<vector>
#include<string>
#include<pthread.h>
#include<queue>
struct ThreadInfo
{
    pthread_t tid;
    std::string name;
};
static const int deafultnum=5; //默认多少个线程
template <class 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 IsQueueEmpty()
    {
      return tasks_.empty();
    }
    std::string GetThreadName(pthread_t tid)
    {
      for (const auto &ti : threads_)
      {
            if (ti.tid == tid)
                return ti.name;
      }
      return "None";
    }
public:
    ThreadPool(int num=deafultnum):threads_(num)
    {
      pthread_mutex_init(&mutex_,nullptr);
      pthread_cond_init(&cond_,nullptr);
    }
    static void *HandleTask(void *args) //所有线程启动后,就会去检测有没有任务,有任务就执行,没任务就休眠
    {
      ThreadPool<T> *tp=static_cast<ThreadPool<T>*>(args);
      std::string name=tp->GetThreadName(pthread_self());
      while (true)
      {
            tp->Lock();
            while(tp->IsQueueEmpty())
            {
                tp->ThreadSleep();
            }
            T t=tp->pop();
            tp->Unlock();
            //当你拿到这个任务,这个任务就是属于你,你不需要在加锁,解锁之间。
            t();
            std::cout<<name<<"run,"<<"result:"<<t.GetResult()<<std::endl;
      }
    }
    void start()
    {
      int num=threads_.size();
      for(int i=0;i<num;i++)
      {
            threads_.name="thread-"+std::to_string(i+1);
            pthread_create(&(threads_.tid),nullptr,HandleTask,this);
      }
    }
    T pop()
    {
      T t=tasks_.front();
      tasks_.pop();
      return t;
    }
    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_; //这是个vector容器,表示有多少个线程
    std::queue<T> tasks_;
    pthread_mutex_t mutex_;
    pthread_cond_t cond_;
};  (3)主函数
#include <iostream>
#include "ThreadPool.hpp"
#include "Task.hpp"
#include<unistd.h>
int main()
{
    ThreadPool<Task> *tp=new ThreadPool<Task>(5);
    tp->start();
    srand(time(nullptr) ^ getpid());
    while(true)
    {
      //1.构建任务
      int x = rand() % 10 + 1;
      usleep(10);
      int y = rand() % 5;
      char op = opers;

      Task t(x, y, op);
      tp->push(t);
      //ThreadPool<Task>::GetInstance()->Push(t);
      //2.交给线程池处理
      std::cout << "main thread make task: " << t.GetTask() << std::endl;
      sleep(1);
    }
} (4)执行效果
 https://i-blog.csdnimg.cn/direct/0b7c2f3f6cf64da2ba6b4f436b53ec40.png
 

 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 线程池的实现与应用