Muduo庫源碼分析(7):線程池

  • 線程池的本質
    生產者與消費者模型,往線程池添加任務相當於生產者,從線程池取出任務相當於消費者,線程池容量相當於有界的緩衝區,所以實現類似於有界緩衝區
class ThreadPool : noncopyable
{
 public:
  typedef std::function<void ()> Task;

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

  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);// 添加任務
  void run(Task&& f);

 private:
  bool isFull() const;
  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::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(std::unique_ptr<muduo::Thread>(new muduo::Thread(
          std::bind(&ThreadPool::runInThread, this), name_+id)));
    threads_[i]->start();
  }
  if (numThreads == 0 && threadInitCallback_)
  {
    threadInitCallback_();
  }
}
// 線程終止,將所有的線程調用join方法,避免殭屍線程
void ThreadPool::stop()
{
  {
  MutexLockGuard lock(mutex_);
  running_ = false;
  notEmpty_.notifyAll();
  }
  std::for_each(threads_.begin(),
           threads_.end(),
           std::bind(&muduo::Thread::join, std::placeholders::_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();// 添加任務成功,喚醒阻塞的消費者線程
  }
}

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

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
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章