muduo源码 ---ThreadPool介绍

muduo源码分析系列 线程池的实现

分析线程池之前,先介绍线程
毕竟线程池里保存着每个线程
先分析Thread类

class Thread : noncopyable
{
 public:
  typedef std::function ThreadFunc;

  explicit Thread(ThreadFunc, const string& name = string());
  // FIXME: make it movable in C++11
  ~Thread();

  void start();
  int join(); // return pthread_join()

  bool started() const { return started_; }
  // pthread_t pthreadId() const { return pthreadId_; }
  pid_t tid() const { return tid_; }
  const string& name() const { return name_; }

  static int numCreated() { return numCreated_.get(); }

 private:
  void setDefaultName();

  bool       started_;
  bool       joined_;
  pthread_t  pthreadId_;
  pid_t      tid_;
  ThreadFunc func_;
  string     name_;
  CountDownLatch latch_;

  static AtomicInt32 numCreated_;
};

仔细观察其实就是把C11的thread相关的方法进行了进一步的封装
但是有个地方 CountDownLatch是什么呢
举个例子:在考试的时候 收卷老师必须要等到所有考生的卷子都收拾好了,才能离开教室。这就是latch的含义,意思是某个线程 必须要等待其他线程完成 才能执行。直接看CountDownLatch的源码

class CountDownLatch :    
{
 public:

  explicit CountDownLatch(int count);

  void wait();

  void countDown();

  int getCount() const;

 private:
  mutable MutexLock mutex_;
  Condition condition_ GUARDED_BY(mutex_);
  int count_ GUARDED_BY(mutex_);
};

看成员: 一个mutex_,一个条件变量condtion,一个公共count_

看方法

CountDownLatch::CountDownLatch(int count)
  : mutex_(),
    condition_(mutex_),
    count_(count)
{
}

void CountDownLatch::wait()
{
  MutexLockGuard lock(mutex_); //上锁
  while (count_ > 0) //等待count变成0
  {
    condition_.wait();
  }
}

void CountDownLatch::countDown()
{
  MutexLockGuard lock(mutex_); //上锁
  --count_; //减少count值
  if (count_ == 0)
  {
    condition_.notifyAll(); //count值为0了 唤醒等待的condtion_变量
  }
}

int CountDownLatch::getCount() const
{
  MutexLockGuard lock(mutex_);
  return count_;
}

看了实现其实很简单:本质就是维护一个共享变量count_,这个count_理解成上面那个例子的学生,每次学生离开教室 那么就调用一次countDown方法,该方法将count-1,如果最后一个学生离开了那么count为0,则调用condtion_的notify方法,唤醒在wait里阻塞的的线程。

知道了latch的功能,就可以开始看Thread相关的方法了。

构造函数

Thread::Thread(ThreadFunc func, const string& n)
  : started_(false),
    joined_(false),
    pthreadId_(0),
    tid_(0),
    func_(std::move(func)),
    name_(n),
    latch_(1) //latch为1 说明只需要等待一个线程countDown即可
{
  setDefaultName();
}

来看最关键的start方法

void Thread::start()
{
  assert(!started_);
  started_ = true;
  // FIXME: move(func_)
  detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);
  if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
  {
    started_ = false;
    delete data; // or no delete?
    LOG_SYSFATAL << "Failed in pthread_create";
  }
  else
  {
    latch_.wait();
    assert(tid_ > 0);
  }
}

这里构造了一个 ThreadData类,然后调用系统api,创建出一个新的线程,且这个线程执行的函数是ThreadData里的detail::startThread()方法(执行用户的func)。
如果创建失败,就delete掉,成功就等待latch里的计数器变为0(如果latch是大于0的话),否则就一直阻塞。

来看看这个类的声明

ThreadData(ThreadFunc func,
             const string& name,
             pid_t* tid,
             CountDownLatch* latch)
    : func_(std::move(func)),
      name_(name),
      tid_(tid),
      latch_(latch)
  { }

  void runInThread()
  {
    *tid_ = muduo::CurrentThread::tid();
    tid_ = NULL;
    latch_->countDown(); //将latch_计数器-1 并且如果等于0的时候 唤醒 latch_.wait线程
    latch_ = NULL;

    muduo::CurrentThread::t_threadName = name_.empty() ? "muduoThread" : name_.c_str();
    ::prctl(PR_SET_NAME, muduo::CurrentThread::t_threadName);
    try
    {
      func_(); //执行func
      muduo::CurrentThread::t_threadName = "finished";
    }
    catch (const Exception& ex)
    {
      muduo::CurrentThread::t_threadName = "crashed";
      fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
      fprintf(stderr, "reason: %s\n", ex.what());
      fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
      abort();
    }
    catch (const std::exception& ex)
    {
      muduo::CurrentThread::t_threadName = "crashed";
      fprintf(stderr, "exception caught in Thread %s\n", name_.c_str());
      fprintf(stderr, "reason: %s\n", ex.what());
      abort();
    }
    catch (...)
    {
      muduo::CurrentThread::t_threadName = "crashed";
      fprintf(stderr, "unknown exception caught in Thread %s\n", name_.c_str());
      throw; // rethrow
    }
  }

上面一堆函数,本质上最重要的还是最终执行了用户传递的func

void* startThread(void* obj)
{
  ThreadData* data = static_cast(obj);
  data->runInThread();
  delete data;
  return NULL;
}

threadDetail类里的一个方法,通过万能指针void*进行强制转化成目标ThreadData类,然后执行runInThread,执行完delete掉这个data。

看完这些设计其实可以明白,为什么要这么设计呢?个人认为还是考虑到线程子资源复用的问题。把线程里执行的函数封装成data类,这样每次执行完毕只需要将data删除掉,而不需要去重复分配和删除掉线程。


image.png

并且 也能说明了。为什么runInThread这里tid需要为空了,因为执行完这个data之后就不需要这个data对象的数据了,latch_置为null也是一样的。

感叹陈硕大佬的代码功底

讲完Thread可以开始讲ThreadPool了

先上代码

class ThreadPool : noncopyable
{
 public:
  typedef std::function Task;

  explicit ThreadPool(const string& nameArg = string("ThreadPool"));
  ~ThreadPool();

  // Must be called before start().
  void setMaxQueueSize(int maxSize) { maxQueueSize_ = maxSize; }
  void setThreadInitCallback(const Task& cb)
  { threadInitCallback_ = cb; }

  void start(int numThreads);
  void stop();

  const string& name() const
  { return name_; }

  size_t queueSize() const;

  void run(Task f);

 private:
  bool isFull() const REQUIRES(mutex_);
  void runInThread();
  Task take();

  mutable MutexLock mutex_;
  Condition notEmpty_ GUARDED_BY(mutex_);
  Condition notFull_ GUARDED_BY(mutex_);
  string name_;
  Task threadInitCallback_;
  std::vector> threads_;
  std::deque queue_ GUARDED_BY(mutex_);
  size_t maxQueueSize_;
  bool running_;
};

以上是类的声明

接下来是对每个成员变量的解释:
mutable MutexLock mutex_
这是作者把mutex互斥锁进行了一个封装

class CAPABILITY("mutex") MutexLock : noncopyable
{
 public:
  MutexLock()
    : holder_(0)
  {
    MCHECK(pthread_mutex_init(&mutex_, NULL)); //初始化调用系统函数init
  }

  ~MutexLock()
  {
    assert(holder_ == 0);
    MCHECK(pthread_mutex_destroy(&mutex_)); //销毁调用系统函数 destroy
  }

  // must be called when locked, i.e. for assertion
  bool isLockedByThisThread() const
  {
    return holder_ == CurrentThread::tid(); //判断这个锁是否是当前线程锁住的
  }

  void assertLocked() const ASSERT_CAPABILITY(this)
  {
    assert(isLockedByThisThread());
  }

  // internal usage

  void lock() ACQUIRE()
  {
    MCHECK(pthread_mutex_lock(&mutex_)); //加锁
    assignHolder();
  }

  void unlock() RELEASE()
  {
    unassignHolder();
    MCHECK(pthread_mutex_unlock(&mutex_)); //解锁
  }

  pthread_mutex_t* getPthreadMutex() /* non-const */
  {
    return &mutex_;  /
  }

 private:
  friend class Condition;

  class UnassignGuard : noncopyable
  {
   public:
    explicit UnassignGuard(MutexLock& owner)
      : owner_(owner)
    {
      owner_.unassignHolder();
    }

    ~UnassignGuard()
    {
      owner_.assignHolder();
    }

   private:
    MutexLock& owner_;
  };

  void unassignHolder()
  {
    holder_ = 0;
  }

  void assignHolder()
  {
    holder_ = CurrentThread::tid();
  }

  pthread_mutex_t mutex_;
  pid_t holder_;
};

直接看这个Mutex类的成员对象:
pthread_mutex_t mutex_;
pid_t holder_;

也就是说 这个Mutex类对pthread_mutex_t 和pid进行了封装,每个mutex对象都有一个锁和 掌握该锁的线程pid。

再看接下来的ThreadPool成员
Condition notEmpty_ GUARDED_BY(mutex_);
Condition notFull_ GUARDED_BY(mutex_);
两个条件变量
string name_;
名称
Task threadInitCallback_;
Task是 typedef std::function Task;
本质是function封装的函数

线程池初始化执行的函数
std::vector> threads_;
线程vector
std::deque queue_ GUARDED_BY(mutex_);
任务队列
size_t maxQueueSize_;
队列最大任务数量
bool running_;
是否在执行

开始分析这个线程池的各个函数

ThreadPool::ThreadPool(const string& nameArg)
  : mutex_(),
    notEmpty_(mutex_),
    notFull_(mutex_),
    name_(nameArg),
    maxQueueSize_(0),
    running_(false)
{
}

成员初始化

void ThreadPool::start(int numThreads)
{
  assert(threads_.empty());
  running_ = true; //设置为运行状态
  threads_.reserve(numThreads); //为线程数量分配vector大小
  for (int i = 0; i < numThreads; ++i) 
  {
    char id[32];
    snprintf(id, sizeof id, "%d", i+1);
    threads_.emplace_back(new muduo::Thread(
          std::bind(&ThreadPool::runInThread, this), name_+id));  //new一个thread对象 加入到 vector中
    threads_[i]->start(); //执行thread
  }
  if (numThreads == 0 && threadInitCallback_)
  {
    threadInitCallback_();  //仅当传参是0且初始化函数是非空 执行初始化函数
  }
}

start 方法 具体thread类后续分析

void ThreadPool::stop()
{
  {
  MutexLockGuard lock(mutex_); // 当前线程池加锁 防止别的线程使用
  running_ = false;  //设置为结束
  notEmpty_.notifyAll(); //唤醒条件
  notFull_.notifyAll();//唤醒条件
  }
  for (auto& thr : threads_)
  {
    thr->join();  //将所有thread结束掉
  }
}

stop方法
上面的
notEmpty_.notifyAll(); //唤醒所有等待 当前条件变量的线程
notFull_.notifyAll();//唤醒所有等待当前条件变量的线程

实际上就是 调用系统api

  void notifyAll()
  {
    MCHECK(pthread_cond_broadcast(&pcond_));  //pcond就是condtion中的成员你变量
  }

通俗的说就是:在线程池中,多个线程可能会同时等待同一个条件变量 ,此时在等待的时候 会有多个线程被挂起,所以调用notifyAll把所有阻塞的线程唤醒,这样才能进行后续的join操作。
但是,在当前线程池线程中,实际上这两个条件变量更多的表示一个当前的状态。
notEmpty_在wait的情况 :说明当前的线程池并不处于非空的情况 ==》 当前线程池是空的(queue是空的)
notEmpty在notify的情况:当前线程池里的queue是非空,说明有task任务需要执行

同理notFull也是一样

如果还不理解可以仔细google一下条件变量的用法

void ThreadPool::run(Task task)
{
  if (threads_.empty())
  {
    task(); //如果当前线程池子没有线程,直接使用线程池所在的线程执行任务
  }
  else
  {
    MutexLockGuard lock(mutex_);
    while (isFull() && running_) //如果当前的线程池子里所有线程都被占用了
    {
      notFull_.wait();  //说明当前的线程池处于满任务状态 阻塞
    }
    if (!running_) return;
    assert(!isFull());

    queue_.push_back(std::move(task)); //任务队列入队
    notEmpty_.notify(); //唤醒所有使用notEmpty条件变量的线程
  }
}

run方法
实际上就是在运行start方法之后,暴露给用户使用的接口。
start方法:设置当前线程池的线程数量。run方法,用户通过封装task传递给run方法,run方法里将task存入queue中,等待runInThread对task进行Take()

ThreadPool::Task ThreadPool::take()
{
  MutexLockGuard lock(mutex_);
  // always use a while-loop, due to spurious wakeup
  while (queue_.empty() && running_)
  {
    notEmpty_.wait();
  }
  Task task;
  if (!queue_.empty())
  {
    task = queue_.front();
    queue_.pop_front();
    if (maxQueueSize_ > 0)
    {
      notFull_.notify(); 
    }
  }
  return task;
}

take方法,实际上就是从queue队列中取出任务,并且返回任务

void ThreadPool::runInThread()
{
  try
  {
    if (threadInitCallback_)
    {
      threadInitCallback_(); //初始化函数
    }
    while (running_)
    {
      Task task(take());
      if (task)
      {
        task();
      }
    }
  }
  catch (const Exception &ex)
  {
    fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
    fprintf(stderr, "reason: %s\n", ex.what());
    fprintf(stderr, "stack trace: %s\n", ex.stackTrace());
    abort();
  }
  catch (const std::exception &ex)
  {
    fprintf(stderr, "exception caught in ThreadPool %s\n", name_.c_str());
    fprintf(stderr, "reason: %s\n", ex.what());
    abort();
  }
  catch (...)
  {
    fprintf(stderr, "unknown exception caught in ThreadPool %s\n", name_.c_str());
    throw; // rethrow
  }
}

runInThread方法,执行take方法,将task真正执行

你可能感兴趣的:(muduo源码 ---ThreadPool介绍)