muduo源碼筆記-base-ThreadPool

1. ThreadPool

ThreadPool類是線程池的封裝。muduo中採用了blocking queue實現的任務隊列,並且啓動數量固定的線程池。每個線程在while(running)的循環中不斷的從隊列中取任務,做任務。任務都是從ThreadPool中的run函數加進來的。

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; }
  // 啓動num個線程
  void start(int numThreads);
  // 停止
  void stop();

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

  size_t queueSize() const;

  // Could block if maxQueueSize > 0
  // There is no move-only version of std::function in C++ as of C++14.
  // So we don't need to overload a const& and an && versions
  // as we do in (Bounded)BlockingQueue.
  // https://stackoverflow.com/a/25408989
  void run(Task f);

 private:
     
  bool isFull() const REQUIRES(mutex_);
  // 運行
  void runInThread();
  // 取一個任務
  Task take();

  mutable MutexLock mutex_;
  // 2個信號量,指示任務隊列的狀態
  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_);
  // 最大隊列數
  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;
  // 直接設置線程數組的大小爲numThreads
  threads_.reserve(numThreads);
  for (int i = 0; i < numThreads; ++i)
  {
    char id[32];
    snprintf(id, sizeof id, "%d", i+1);
    // 創建numThreads個線程,運行的任務是runInThread
    // 注意Thread的構造函數是(func_, name)
    // 因此runInThread就是傳入ThreadData中的任務
    threads_.emplace_back(new muduo::Thread(
          std::bind(&ThreadPool::runInThread, this), name_+id));
    threads_[i]->start();
  }
  // 如果numThreads == 0,則直接執行初始任務
  if (numThreads == 0 && threadInitCallback_)
  {
    threadInitCallback_();
  }
}

// 停止線程池
void ThreadPool::stop()
{
  {
  MutexLockGuard lock(mutex_);
  running_ = false;
  notEmpty_.notifyAll();
  }
  // 等待每個正在運行的線程
  for (auto& thr : threads_)
  {
    thr->join();
  }
}

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

// 往隊列中加任務,讓線程池中的線程去運行
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
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章