【muduo】线程池

ThreadPool.h

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#ifndef MUDUO_BASE_THREADPOOL_H
#define MUDUO_BASE_THREADPOOL_H

#include <muduo/base/Condition.h>
#include <muduo/base/Mutex.h>
#include <muduo/base/Thread.h>
#include <muduo/base/Types.h>

#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/ptr_container/ptr_vector.hpp>

#include <deque>

namespace muduo
{

class ThreadPool : boost::noncopyable
{
 public:
  typedef boost::function<void ()> 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;

  // Could block if maxQueueSize > 0
  // 运行一个任务,将任务放到任务队列中
  void run(const Task& f);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
  void run(Task&& f);
#endif

 private:

  // 任务队列是否已满
  bool isFull() const;

  // 线程处理函数,这个函数用来取出线程池任务队列中的任务,然后进行处理
  void runInThread();

  // 从任务队列中取出任务
  Task take();

  // 互斥量,用户保护任务队列,和下面两个条件变量
  mutable MutexLock mutex_;
  // 条件变量,任务队列不空,可以取任务
  Condition notEmpty_;
  // 条件变量,任务队列不满,可以添加任务
  Condition notFull_;
  // 线程池名称
  string name_;
  //初始化线程时所用回调函数
  Task threadInitCallback_;
  // 线程列表 ptr_vector是boost库的指针容器
  boost::ptr_vector<muduo::Thread> threads_;
  // 任务队列
  std::deque<Task> queue_;
  // 任务队列的最大数值
  size_t maxQueueSize_;
  // 线程池是否正在运行
  bool running_;
};

}

#endif

ThreadPool.cpp

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include <muduo/base/ThreadPool.h>

#include <muduo/base/Exception.h>

#include <boost/bind.hpp>
#include <assert.h>
#include <stdio.h>

using namespace muduo;

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

ThreadPool::~ThreadPool()
{
  if (running_)
  {
    stop();
  }
}

// 启动线程池,线程池中创建指定数目的线程
void ThreadPool::start(int numThreads)
{
  assert(threads_.empty());
  running_ = true;
  threads_.reserve(numThreads);
  for (int i = 0; i < numThreads; ++i)
  {
    char id[32];
    snprintf(id, sizeof id, "%d", i+1);

    //创建线程并启动
    threads_.push_back(new muduo::Thread(
          boost::bind(&ThreadPool::runInThread, this), name_+id));
    threads_[i].start();
  }

  // 如果要求创建线程的数目为0,并且设定了线程池初始化回调函数,就调用线程池初始化回调函数
  if (numThreads == 0 && threadInitCallback_)
  {
    threadInitCallback_();
  }
}

// 停止线程池
void ThreadPool::stop()
{
  {
  MutexLockGuard lock(mutex_);
  running_ = false;
  notEmpty_.notifyAll();
  }

  // 阻塞并等待所有线程执行完毕
  for_each(threads_.begin(),
           threads_.end(),
           boost::bind(&muduo::Thread::join, _1));
}

// 获取线程池任务队列数目
size_t ThreadPool::queueSize() const
{
  MutexLockGuard lock(mutex_);
  return queue_.size();
}

// 处理任务
void ThreadPool::run(const Task& task)
{
  if (threads_.empty())
  {
    //如果线程池中没有处理线程,就直接执行任务,注意并没有重新开始一个新的线程,所以是以阻塞方式进行
    task();
  }
  else
  {
    // 否则,就等待任务队列不满的情况下,将任务加入任务队列,并通知线程任务队列已经非空
    MutexLockGuard lock(mutex_);
    while (isFull())
    {
      notFull_.wait();
    }
    assert(!isFull());

    queue_.push_back(task);
    notEmpty_.notify();
  }
}

// C++11 的右值引用版本
#ifdef __GXX_EXPERIMENTAL_CXX0X__
void ThreadPool::run(Task&& task)
{
  if (threads_.empty())
  {
    task();
  }
  else
  {
    MutexLockGuard lock(mutex_);
    while (isFull())
    {
      notFull_.wait();
    }
    assert(!isFull());

    queue_.push_back(std::move(task));
    notEmpty_.notify();
  }
}
#endif

// 从任务对垒中取出一个任务
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;
}

// 判断任务队列是否已满
bool ThreadPool::isFull() const
{
  mutex_.assertLocked();
  return maxQueueSize_ > 0 && queue_.size() >= maxQueueSize_;
}

// 这个就是线程处理函数,主要功能就是开始一个循环,然后从任务队列中取出任务,并执行任务
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
  }
}

注意点:

线程池的创建和任务处理,用到了生产者消费者模型。
任务队列中新加任务是生产者进行生产,线程池中处理线程处理任务是消费者进行消费。注意任务队列的同步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值