muduoソース分析-EventLoopThreadPoolとEventLoopThread
21516 ワード
これからEventLoop関連のクラスを書きますが、EventLoopThreadPoolを先に書くことにしました.
TcpServerは所有関係から見るとEventLoopThreadPoolを持っているので、そしてEventLoopThreadPoolはいくつかのEventLoopThread(TcpServer初期化時に決定)を持っています.EventLoopを直接持っているかどうかについては、持っていると言ってもいいのではないかと思いますが、これらのEventLoopを操作していないので、管理器としか言いようがありません.プールを実現するというものです.
メンバー#メンバー#
baseLoop_メインEventLoopをロックするために使われていると思います.いつでもメインLoopを手に入れる必要があるかもしれませんが、後でEventLoop*EventLoopThreadPool::getNextLoop()を読むと
実はもう一つの役割は、スレッドが1つのメインEventLoopしかなく、他の作業スレッドを初期化していない場合、このgetNextLoop()を正しく実行させることであり、この場合のために別の関数を書くことなく、プログラムの複雑さを低減したり、多重性を増やしたりすることである.ここでは,プライマリスレッドとワークスレッドが厳格に分離管理されていることがわかる.
他のメンバーについては、意味がはっきりしているので、あまり説明しません.
EventLoopThreadPool初期化およびstart
私は構造をinitやstartと一緒に置いておきましょうが、実は内容は多くなく複雑ではありません.
構造的にはbaseLoop_デフォルトはNULLに初期化されていますが、実際にはTcpServerからEventLoopが渡され、startもデフォルトはfalseで、start、nextが0に初期化される必要があります.最初にタスクに追加されたのはもちろん0番目のスレッドなので、実はこの初期化は何でもいいと思います.負荷均衡が実現できるからです.しかし!防止のために、実際のスレッド数-1よりも大きい数に初期化したので、0を初期化した.そして後の私について説明する必要はないと思います.論理も難しくないからです.
EventLoopThread
次にEventLoopThreadというクラスを見てみましょう.
メンバー#メンバー#
まず簡単に言えばexiting_この変数は、実はこのThreadが脱退したかどうかをマークしていますが、外部ではこの変数を引用する場所が見つからないので、具体的な用途はないかもしれません.純粋にマークとして使われているだけです.他のメンバーについてはコメントしていますが、次にEventLoopThreadの関数を見てみましょう.
startLoop
この関数に戻ると、実は簡単です.新しいスレッドを開き、EventLoopThread::threadFuncを実行します.では、この関数を見てみましょう.
threadFunc
ここのロックの論理を簡単に言えば、簡単に言えば、スレッドが起動した後、loop_すぐに!=NULLになって、最後にまたloop_=NULL、目的は前のstartLoop関数をブロックすることです.
正直このcallback、私はずっと具体的な実装を見つけていません.それはEventLoopThreadPoolに登録されていますが、TcpServerにstartを呼び出されたときに入ってきましたが、TcpServerがstartを呼び出すときにパラメータを渡したことがないので、ちょっと不思議ですが、この問題は解決しなければなりません.関数ポインタが伝わったと思いますが、このポインタは実装された関数ではなく、空です.後でブレークポイントを打って、ここが実行されるかどうか見てみましょう.
もう一度よく検討してください.前の内容に戻るとstartLoopがloop_を返しているのが見えますが、だからloopを待つ必要がある割り当てられてから戻ることができます.そうしないとNULLに戻るのは問題があります.
stopLoop
具体的なフラグを設定し、joinがクリーンアップし、EventLoopのquit_フラグがtrueの場合、EventLoopのwhileメインループは終了されますが、具体的にはEventLoopのコードを見てみましょう.見ればわかりますが、ここでは展開しません.まだEventLoopについて議論していないからです.そしてEventLoopのLoop関数が実行されると、join()でメモリをクリーンアップし、解放することができます.これが終了の方法です.私はjoinの紹介を探しました.ここでみんな見てもいいです.
TcpServerは所有関係から見るとEventLoopThreadPoolを持っているので、そしてEventLoopThreadPoolはいくつかのEventLoopThread(TcpServer初期化時に決定)を持っています.EventLoopを直接持っているかどうかについては、持っていると言ってもいいのではないかと思いますが、これらのEventLoopを操作していないので、管理器としか言いようがありません.プールを実現するというものです.
メンバー#メンバー#
private:
EventLoop* baseLoop_;// ,
std::string name_;
bool started_;//
int numThreads_;//
int next_;//
std::vector<std::shared_ptr<EventLoopThread> > threads_;//
std::vector<EventLoop*> loops_;// EventLoop
baseLoop_メインEventLoopをロックするために使われていると思います.いつでもメインLoopを手に入れる必要があるかもしれませんが、後でEventLoop*EventLoopThreadPool::getNextLoop()を読むと
EventLoop* EventLoopThreadPool::getNextLoop()
{
baseLoop_->assertInLoopThread();
//assert(started_);
if (!started_)
return NULL;
EventLoop* loop = baseLoop_;
if (!loops_.empty())// , , , TcpServer , 0,
{
// round-robin
loop = loops_[next_];// ,
++next_;
if (implicit_cast<size_t>(next_) >= loops_.size())// 0
{
next_ = 0;
}
}
return loop;
}
実はもう一つの役割は、スレッドが1つのメインEventLoopしかなく、他の作業スレッドを初期化していない場合、このgetNextLoop()を正しく実行させることであり、この場合のために別の関数を書くことなく、プログラムの複雑さを低減したり、多重性を増やしたりすることである.ここでは,プライマリスレッドとワークスレッドが厳格に分離管理されていることがわかる.
他のメンバーについては、意味がはっきりしているので、あまり説明しません.
EventLoopThreadPool初期化およびstart
私は構造をinitやstartと一緒に置いておきましょうが、実は内容は多くなく複雑ではありません.
EventLoopThreadPool::EventLoopThreadPool()
: baseLoop_(NULL),
started_(false),
numThreads_(0),
next_(0)
{
}
EventLoopThreadPool::~EventLoopThreadPool()
{
// Don't delete loop, it's stack variable
}
void EventLoopThreadPool::Init(EventLoop* baseLoop, int numThreads)
{
numThreads_ = numThreads;
baseLoop_ = baseLoop;
}
void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
//assert(baseLoop_);
if (baseLoop_ == NULL)
return;
assert(!started_);
if (started_)// start
return;
baseLoop_->assertInLoopThread();
started_ = true;
for (int i = 0; i < numThreads_; ++i)
{
char buf[128];
snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i);// , buf
std::shared_ptr<EventLoopThread> t(new EventLoopThread(cb, buf));
//EventLoopThread* t = new EventLoopThread(cb, buf);
threads_.push_back(t);//
loops_.push_back(t->startLoop());// loop EventLoop , loops_
}
if (numThreads_ == 0 && cb)
{
cb(baseLoop_);//ThreadInitCallback,
}
}
構造的にはbaseLoop_デフォルトはNULLに初期化されていますが、実際にはTcpServerからEventLoopが渡され、startもデフォルトはfalseで、start、nextが0に初期化される必要があります.最初にタスクに追加されたのはもちろん0番目のスレッドなので、実はこの初期化は何でもいいと思います.負荷均衡が実現できるからです.しかし!防止のために、実際のスレッド数-1よりも大きい数に初期化したので、0を初期化した.そして後の私について説明する必要はないと思います.論理も難しくないからです.
EventLoopThread
次にEventLoopThreadというクラスを見てみましょう.
メンバー#メンバー#
private:
void threadFunc();
EventLoop* loop_;// EventLoop
bool exiting_;//
std::shared_ptr<std::thread> thread_;//
std::mutex mutex_;//
std::condition_variable cond_;//cond_.wait() 。
ThreadInitCallback callback_;
};
まず簡単に言えばexiting_この変数は、実はこのThreadが脱退したかどうかをマークしていますが、外部ではこの変数を引用する場所が見つからないので、具体的な用途はないかもしれません.純粋にマークとして使われているだけです.他のメンバーについてはコメントしていますが、次にEventLoopThreadの関数を見てみましょう.
startLoop
EventLoop* EventLoopThread::startLoop()
{
//assert(!thread_.started());
//thread_.start();
thread_.reset(new std::thread(std::bind(&EventLoopThread::threadFunc, this)));//
{
std::unique_lock<std::mutex> lock(mutex_);
while (loop_ == NULL)
{
cond_.wait(lock);//
}
}
return loop_;
}
std::condition_variable
を簡単に紹介します.ここでは、良いc++条件変数cond_を紹介します.wait()は、スレッドをスリープさせ、notify_を使用することができます.one()またはnotify_all()が起動し、もう一つはstd::lock_を使用できません.guardでstd::unique_を使用lock、具体的な原因は、上のリンクで説明したように、waitはunlock()を呼び出してから寝ます.std::lock_guardはこの実現はありませんよ.この関数に戻ると、実は簡単です.新しいスレッドを開き、EventLoopThread::threadFuncを実行します.では、この関数を見てみましょう.
threadFunc
void EventLoopThread::threadFunc()
{
EventLoop loop;
if (callback_)
{
callback_(&loop);
}
{
//
std::unique_lock<std::mutex> lock(mutex_);// std::unique_lock , unlock lock
loop_ = &loop;
cond_.notify_all();
}
loop.loop();//
//assert(exiting_);
loop_ = NULL;
}
ここのロックの論理を簡単に言えば、簡単に言えば、スレッドが起動した後、loop_すぐに!=NULLになって、最後にまたloop_=NULL、目的は前のstartLoop関数をブロックすることです.
正直このcallback、私はずっと具体的な実装を見つけていません.それはEventLoopThreadPoolに登録されていますが、TcpServerにstartを呼び出されたときに入ってきましたが、TcpServerがstartを呼び出すときにパラメータを渡したことがないので、ちょっと不思議ですが、この問題は解決しなければなりません.関数ポインタが伝わったと思いますが、このポインタは実装された関数ではなく、空です.後でブレークポイントを打って、ここが実行されるかどうか見てみましょう.
if (callback_)
{
callback_(&loop);
}
もう一度よく検討してください.前の内容に戻るとstartLoopがloop_を返しているのが見えますが、だからloopを待つ必要がある割り当てられてから戻ることができます.そうしないとNULLに戻るのは問題があります.
loop_ = &loop;
が実行された後、notify_が表示されます.all()プロセスを起動します.stopLoop
void EventLoopThread::stopLoop()
{
if (loop_ != NULL)
loop_->quit();
// eventLoop quit_ true, , eventLoop
thread_->join();// this , 。
}
具体的なフラグを設定し、joinがクリーンアップし、EventLoopのquit_フラグがtrueの場合、EventLoopのwhileメインループは終了されますが、具体的にはEventLoopのコードを見てみましょう.見ればわかりますが、ここでは展開しません.まだEventLoopについて議論していないからです.そしてEventLoopのLoop関数が実行されると、join()でメモリをクリーンアップし、解放することができます.これが終了の方法です.私はjoinの紹介を探しました.ここでみんな見てもいいです.