moduo-base 源码分析(四)——threadpool

主要的成员变量和成员函数

typedef std::function<void ()> Task;		//boost::function,task 是 void () 函数指针

//构造函数和析构函数
explicit ThreadPool(const string& nameArg = string("ThreadPool"));
~ThreadPool();

void setThreadInitCallback(const Task& cb)
{ threadInitCallback_ = cb; }

//启动和关闭线程池
void start(int numThreads);
void stop();  

//向任务队列中添加任务
void run(Task f);

//线程池中的线程执行函数
void runInThread();    
 
//从任务队列中获取任务 
Task take();            

//成员变量
mutable MutexLock mutex_;       		   //互斥锁
Condition notEmpty_ GUARDED_BY(mutex_);   //有界队列,判断是否为满
Condition notFull_ GUARDED_BY(mutex_);    //判断是否为空
string name_;         					  //线程池名称
Task threadInitCallback_;				  //线程初始化回调函数
std::vector<std::unique_ptr<muduo::Thread>> threads_;   //线程队列,线程的生命周期由线程池控制
std::deque<Task> queue_ GUARDED_BY(mutex_);     //任务队列,fifo
size_t maxQueueSize_;					  //任务队列的最大值
bool running_;        					  //判断线程池是否处于运行状态
  • 线程池中线程数是固定不变的;
  • 队列可以是有界的也可以是无界的,有 maxQueueSize_ 决定;

具体函数解析

构造函数与析构函数

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

ThreadPool::~ThreadPool()
{
  if (running_)
  {
  	// 调用 stop 函数进行关闭
    stop();		
  }
}

线程池的启动

// 调用 start 时就要明确线程数量
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);

	//调用线程的构造函数,传递线程执行函数以及线程池,这里的线程是已经封装好的,见 Thread.h
	//boost::bind 传递 ThreadPool::runInThread 函数,默认有一个 this 指针,thread 的 runfunc 要求类型是 void ()
	//threads_ 中的每个元素都是 unique_ptr,unique_ptr 没有拷贝构造函数和赋值构造函数
	//emplace_back 是调用显式构造函数
	//也可以使用 push_back(new unique_ptr<muduo::Thread>(new muduo::thread())) 但是效率会比 emplace_back 低
    threads_.emplace_back(new muduo::Thread(
          std::bind(&ThreadPool::runInThread, this), name_+id));
    
    //调用 runInThread 函数
	threads_[i]->start();     
  }
  if (numThreads == 0 && threadInitCallback_)
  {
    threadInitCallback_();
  }
}



void ThreadPool::runInThread()
{
  //异常处理
  try
  {
    if (threadInitCallback_)
    {
      threadInitCallback_();
    }
    //线程停止运行的条件是线程池停止工作,所以线程池的析构函数需要将 running_ 置为 0
    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_);
  //正常只有一个条件,外加一个是确定线程还在运行
  while (queue_.empty() && running_)
  {
    //等待任务队列不为空
    notEmpty_.wait();
  }
	
  Task task;
  if (!queue_.empty())
  {
    task = queue_.front();
    queue_.pop_front();

	//如果 maxQueueSize_  为空则是无界队列,否则是有界队列
    if (maxQueueSize_ > 0)
    {
      notFull_.notify();    
    }
  }
  return task;
}
  1. 线程池运行时会创建 numThreads 个线程对象,随后每个线程开始运行;
  2. 每个线程会一直从任务队列中获取任务并执行操作,直到线程池停止;
  3. 任务队列会根据 maxQueueSize_ 的值判断是有界还是无界,决定是否唤醒一个条件变量;

向任务队列中添加任务


//生产者
void ThreadPool::run(Task task)
{
  //如果线程数量为空,直接执行该任务,否则添加进任务队列
  if (threads_.empty())
  {
    task();
  }
  else
  {
    MutexLockGuard lock(mutex_);
    //如果 maxQueueSize_ == 0,则 isFull 返回 false
    //如果 maxQueueSize_ == 0, 就是无界队列
    while (isFull() && running_)
    {
      notFull_.wait();
    }
    //线程池停止工作,不添加任务
    //此时不会传递信号让线程运行,所以析构函数需要唤醒一次
    if (!running_) return;
    assert(!isFull());

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

bool ThreadPool::isFull() const
{
  mutex_.assertLocked();
  return maxQueueSize_ > 0 && queue_.size() >= maxQueueSize_;
}

关闭线程池

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

void ThreadPool::stop()
{
  {
  MutexLockGuard lock(mutex_);
  running_ = false;
  notEmpty_.notifyAll();
  notFull_.notifyAll();
  }
  for (auto& thr : threads_)
  {
    thr->join();			//等待线程结束
  }
}

test

#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;
  
  //创建线程池对象,传递 name
  muduo::ThreadPool pool("MainThreadPool");
  
  //设置队列长度
  pool.setMaxQueueSize(maxSize);
  
  //创建 5 个线程,运行线程,等待任务
  pool.start(5);

  LOG_WARN << "Adding";

  //添加两个 print 任务
  pool.run(print);
  pool.run(print);

  //添加 100 个任务
  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)));
  }
  LOG_WARN << "Done";

  muduo::CountDownLatch latch(1);
  //子线程减少count
  pool.run(std::bind(&muduo::CountDownLatch::countDown, &latch));
  //父线程等待 count 为 0
  latch.wait();
  pool.stop();
}

void longTask(int num)
{
  LOG_INFO << "longTask " << num;
  muduo::CurrentThread::sleepUsec(3000000);
}

void test2()
{
  LOG_WARN << "Test ThreadPool by stoping early.";
  muduo::ThreadPool pool("ThreadPool");
  pool.setMaxQueueSize(5);

  pool.start(3);

  //lambda 表达式,创建一个线程,子线程给父线程的线程池中添加任务
  muduo::Thread thread1([&pool]()
  {
    for (int i = 0; i < 20; ++i)
    {
      pool.run(std::bind(longTask, i));
    }
  }, "thread1");
  thread1.start();

  muduo::CurrentThread::sleepUsec(5000000);
  LOG_WARN << "stop pool";
  pool.stop();  // early stop

  thread1.join();
  // run() after stop()
  pool.run(print);
  LOG_WARN << "test2 Done";
}

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

/*
  线程池运行步骤如下:
  
  muduo::ThreadPool pool(threadpool_name);
  
  pool.setMaxQueueSize(maxSize);
  
 
  pool.start(thread_num);

  pool.run(task);

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值