【Linux】线程池|单例模式|STL、智能指针线程安全|读者写者问题

07-09 1340阅读

 > 作者:დ旧言~

> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:理解【Linux】线程池|单例模式|STL、智能指针线程安全|读者写者问题。

> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!

> 专栏选自:Linux初阶

> 望小伙伴们点赞👍收藏✨加关注哟💕💕

【Linux】线程池|单例模式|STL、智能指针线程安全|读者写者问题​​

🌟前言

今天是Linux的最后一片博客,相信大家已经坚持下来了,还是那句话 " 学,然后知不足 "!!!

⭐主体

学习【Linux】线程池|单例模式|STL、智能指针线程安全|读者写者问题咱们按照下面的图解:

【Linux】线程池|单例模式|STL、智能指针线程安全|读者写者问题

​🌙 线程池

线程概念:

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

线程池的应用场景:

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

线程池示例:

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

    ​🌙 线程池代码

    Thread.hpp的简单封装线程,下面我们进行简单的验证:

    • Thread类主要成员变量是线程名,函数,线程参数,参数ID以及对应编号。
    • Thread类提供了一个无参构造,完成对成员变量name的赋值。
    • 同时,对外主要提供了start接口和join接口,对于join接口就是线程等待,而对于start接口就是创建线程的接口,在外部如果调用的话我们需要传入对应的函数以及线程对应的参数。

      代码如下:

      pragma once
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      namespace ThreadNs
      {
          typedef std::function func_t;
          const int num = 1024;
          class Thread
          {
          private:
              static void *start_routine(void *args)
              {
                  Thread *td = static_cast(args);
                  return td->callback();
              }
          public:
              Thread()
              {
                  char buffer[num];
                  snprintf(buffer, sizeof buffer, "thread-%d", threadnum++);
                  _name = buffer;
              }
              void start(func_t func, void *args)
              {
                  _func = func;
                  _args = args;
                  int n = pthread_create(&_tid, nullptr, start_routine, this);
              }
              void join()
              {
                  int n = pthread_join(_tid, nullptr);
                  assert(n == 0);
                  (void)n;
              }
              std::string threadname()
              {
                  return _name;
              }
              void *callback()
              {
                  return _func(_args);
              }
              ~Thread()
              {
              }
          private:
              std::string _name;
              void *_args;
              func_t _func;
              pthread_t _tid;
              static int threadnum;
          };
          int Thread::threadnum = 1;
      }
      

      对于任务队列,可以由多个线程进行访问,我们就需要加锁保护了,把之前写过的锁的小组件引入进来:

      LockGuard.hpp:

      #include 
      #include 
      class Mutex
      {
      public:
          Mutex(pthread_mutex_t*lock_p=nullptr)
          :lock_p_(lock_p)
          {}
          void lock()
          {
              if(lock_p_) pthread_mutex_lock(lock_p_);
          }
          void unlock()
          {
              if(lock_p_) pthread_mutex_unlock(lock_p_);
          }
          ~Mutex()
          {}
      private:
          pthread_mutex_t * lock_p_;
      };
      class LockGuard
      {
      public:
          LockGuard(pthread_mutex_t*mutex)
          :mutex_(mutex)
          {
              mutex_.lock();
          }
          ~LockGuard()
          {
              mutex_.unlock();
          }
      private:
          Mutex mutex_;
      };
      

      线程池代码如下:创建一批线程时,我们需要实现线程的运行函数static void*handlerTask,之所以是静态的,是因为我们要把这个运行函数传递给Thread类中的func_,不能有this指针,所以是静态成员函数。而没有this指针,我们无法访问ThreadPool里面的成员变量,所以需要封装接口供其调用。

      ThreadPool.hpp:

      #pragma once
      #include "Thread.hpp"
      #include "LockGuard.hpp"
      #include 
      #include 
      #include 
      #include 
      #include 
      using namespace ThreadNs;
      const int gnum = 3;
      template 
      class ThreadPool;
      template 
      class ThreadData
      {
      public:
          ThreadPool *threadpool;
          std::string name;
      public:
          ThreadData(ThreadPool *tp, const std::string &n) : threadpool(tp), name(n)
          { }
      };
      template 
      class ThreadPool
      {
      private:
          static void *handlerTask(void *args)
          {
              ThreadData *td = (ThreadData *)args;
              ThreadPool *threadpool = static_cast(args);
              while (true)
              {
                  T t;
                  {
                      LockGuard lockguard(td->threadpool->mutex());
                      while(td->threadpool->isQueueEmpty())
                      {
                          td->threadpool->threadWait();
                      }
                      t = td->threadpool->pop(); 
                  }
                  std::cout name 
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]