muduo_base代码剖析之ThreadPool线程池

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84323546

1. 线程池

线程池的问题本质上也是生产者消费者模型问题

  1. 生产者生产产品的过程,实际上就是由程序员向任务队列中添加任务的过程(需要程序员控制),实现代码见下:
  1. print函数是程序员自己手动定义的任务函数
  2. run(Task task)接口的功能是将task添加到任务队列中,并唤醒等待任务的线程
  pool.run(print); //该句话表示:程序员手动将print任务添加到任务队列
  
  for (int i = 0; i < 100; ++i)
  {
    char buf[32];
    snprintf(buf, sizeof buf, "task %d", i);
    pool.run(std::bind(printString, std::string(buf)));
  }
  1. 消费者是线程池中的线程,(不需要程序员控制),当任务队列中有任务时,将会唤醒线程池中的空闲线程
    在这里插入图片描述

2. muduo线程池代码详解:ThreadPool

// ThreadPool.h文件
class ThreadPool : noncopyable
{
 public:
  typedef std::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(Task f);

 private:
  bool isFull(); //任务队列满
  void runInThread();
  Task take();

  mutable MutexLock mutex_;
  Condition notEmpty_;
  Condition notFull_;
  string name_;
  
  Task threadInitCallback_;
  
  std::vector<std::unique_ptr<muduo::Thread>> threads_;
  
  std::deque<Task> queue_;
  size_t maxQueueSize_; //任务队列的容量
  
  bool running_;
};
// ThreadPool.cc文件
#include <muduo/base/ThreadPool.h>

#include <muduo/base/Exception.h>

#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();
  }
}

// 1. 创建numThreads个线程,并放入线程队列
// 2. 调用threads_[i]->start(),使每个线程都启动线程函数runInThread()
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_.emplace_back(new muduo::Thread(
          std::bind(&ThreadPool::runInThread, this), name_+id));
    threads_[i]->start();
  }
  if (numThreads == 0 && threadInitCallback_)
  {
    threadInitCallback_();
  }
}

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
  }
}

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;
}

void ThreadPool::stop()
{
  {
    MutexLockGuard lock(mutex_);
    running_ = false;
    notEmpty_.notifyAll();//通知所有的等待线程
  }
  
  for (auto& thr : threads_) //等待所有的线程执行完任务后再退出
  {
    thr->join();
  }
  //等价于  for_each(thread_.begin(),thread_.end(),
		//      boost::bind(&muduo::Thread::join,_1));
}

size_t ThreadPool::queueSize() const
{
  MutexLockGuard lock(mutex_);
  return queue_.size();
}

//
void ThreadPool::run(Task task)
{
  if (threads_.empty()) //创建了0个线程,即没有线程池,只有一个主线程
  {
    task(); //主线程强制执行该任务
  }
  else// 如果线程池中有空闲线程,就将任务添加到任务队列
  {
    MutexLockGuard lock(mutex_);
    while (isFull())
    {
      notFull_.wait();
    }
    assert(!isFull());

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

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

3. 线程池测试示例

#include <muduo/base/ThreadPool.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Logging.h>

#include <stdio.h>
#include <unistd.h>  // usleep

void print()
{
  printf("tid=%d\n", muduo::CurrentThread::tid());
}

void printString(const std::string& str)
{
  LOG_INFO << str;
  usleep(100*1000);
}

void test(int maxSize)
{
  LOG_WARN << "Test ThreadPool with max queue size = " << maxSize;
  muduo::ThreadPool pool("MainThreadPool");
  pool.setMaxQueueSize(maxSize); //设置任务队列的大小
  pool.start(5); //创建5个线程,并启动线程

  LOG_WARN << "Adding";
  //向线程池中添加自定义的无参的print任务,并唤醒线程池中的空闲线程
  pool.run(print); 
  pool.run(print);
  for (int i = 0; i < 100; ++i)
  {
    char buf[32];
    snprintf(buf, sizeof buf, "task %d", i);
    //向线程池中添加自定义的有参数的printString任务,并唤醒线程池中的空闲线程
    pool.run(std::bind(printString, std::string(buf)));
  }
  LOG_WARN << "Done";

  //向线程池中添加CountDownLatch类中的成员函数countDown(),并唤醒线程池中的空闲线程
  muduo::CountDownLatch latch(1);
  pool.run(std::bind(&muduo::CountDownLatch::countDown, &latch));
  latch.wait();
  
  pool.stop();
}

int main()
{
  test(0);
  test(1);
  test(5);
  test(10);
  test(50);
}

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/84323546
今日推荐