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