上篇說(shuō)了多線程處理的概述椿争,這篇說(shuō)說(shuō)具體實(shí)現(xiàn)典唇。
muduo的多線程是由線程池中啟動(dòng)的弥雹。線程池類EventLoopThreadPool在TcpServer類中創(chuàng)建一個(gè)心得實(shí)例猜极。發(fā)現(xiàn)在muduo中,各種類的關(guān)系基本上引用和包含即組合關(guān)系色查,很少有派生關(guān)系的,沒(méi)有繼承關(guān)系就沒(méi)有虛函數(shù)的應(yīng)用了撞芍。
可能陳碩覺(jué)得繼承關(guān)系比較復(fù)雜秧了,耦合度太高,破壞整體設(shè)計(jì)序无。但是我覺(jué)得muduo中那么多不同種類的智能指針验毡,還有基于boost或std的函數(shù)綁定衡创,本身就夠復(fù)雜的了。所以我打算有時(shí)間用c語(yǔ)言來(lái)改寫一下muduo晶通,把那些智能指針璃氢,函數(shù)綁定等弱化,只關(guān)注網(wǎng)絡(luò)框架本身狮辽。不過(guò)說(shuō)實(shí)話一也,muduo本身就是基于C++的,用智能指針和函數(shù)綁定很正常喉脖,而且人家還用得非常到位椰苟。所以要想往C++方面發(fā)展,還是得精通上面的知識(shí)树叽。我本身是搞C++舆蝴,而且搞了很久,而且決定一直搞下去题诵。不過(guò)在接觸c項(xiàng)目洁仗,腳本語(yǔ)言,還有現(xiàn)代網(wǎng)絡(luò)并發(fā)語(yǔ)言golang之后性锭,還是覺(jué)得用C++寫項(xiàng)目開(kāi)發(fā)效率太低了赠潦,而且很難駕馭,指針就是一個(gè)雷區(qū)篷店。精通C++的時(shí)間與其收獲性價(jià)比是很低的祭椰,所以我決定以后不再畫太多的時(shí)間在C++上,對(duì)于C++項(xiàng)目疲陕,我只是關(guān)注其框架本身方淤。
好了廢話少說(shuō)。EventLoopThreadPool的start在TcpServer的start中調(diào)用蹄殃,他會(huì)創(chuàng)建n個(gè)線程并啟動(dòng)携茂,n是EventLoopThreadPool的成員變量,可以配置诅岩,也可以是0個(gè)讳苦。每個(gè)線程是EventLoopThread的實(shí)例。而EventLoopThread有個(gè)組合對(duì)象Thread吩谦,他才是線程創(chuàng)建和啟動(dòng)真正的地方鸳谜。代碼如下:
void TcpServer::start()
{
if (started_.getAndSet(1) == 0)
{
threadPool_->start(threadInitCallback_);
assert(!acceptor_->listenning());
loop_->runInLoop(
std::bind(&Acceptor::listen, get_pointer(acceptor_)));
}
}
void EventLoopThreadPool::start(const ThreadInitCallback& cb)
{
assert(!started_);
baseLoop_->assertInLoopThread();
started_ = true;
for (int i = 0; i < numThreads_; ++i)
{
char buf[name_.size() + 32];
snprintf(buf, sizeof buf, "%s%d", name_.c_str(), i);
EventLoopThread* t = new EventLoopThread(cb, buf);
threads_.push_back(std::unique_ptr<EventLoopThread>(t));
loops_.push_back(t->startLoop());
}
if (numThreads_ == 0 && cb)
{
cb(baseLoop_);
}
}
void Thread::start()
{
assert(!started_);
started_ = true;
// FIXME: move(func_)
detail::ThreadData* data = new detail::ThreadData(func_, name_, &tid_, &latch_);
if (pthread_create(&pthreadId_, NULL, &detail::startThread, data))
{
started_ = false;
delete data; // or no delete?
LOG_SYSFATAL << "Failed in pthread_create";
}
else
{
latch_.wait();
assert(tid_ > 0);
}
}
Thread的線程函數(shù)已經(jīng)綁定在了EventLoopThread::threadFunc里,這個(gè)是在EventLoopThread構(gòu)造函數(shù)是初始化的式廷。代碼如下:
void EventLoopThread::threadFunc()
{
EventLoop loop;
if (callback_)
{
callback_(&loop);
}
{
MutexLockGuard lock(mutex_);
loop_ = &loop;
cond_.notify();
}
loop.loop();
//assert(exiting_);
loop_ = NULL;
}
這里有個(gè)問(wèn)題是咐扭,主線程會(huì)加入新線程的loop實(shí)例,而這個(gè)loop實(shí)例又是在線程的線程函數(shù)里創(chuàng)建的。所以很顯然主線程和新線程有個(gè)同步的過(guò)程蝗肪,并且多個(gè)新線程之間有臨界區(qū)的問(wèn)題袜爪。這些是用條件變量pthread_cond_t和互斥變量mutext來(lái)實(shí)現(xiàn)的。
新線程線程函數(shù)在棧上申明一個(gè)EventLoop對(duì)象之后薛闪,便通知主線程了辛馆。這使得主線程返回,而新線程函數(shù)中l(wèi)oop開(kāi)始運(yùn)作循環(huán)了豁延。那么主線程又是如何跨線程調(diào)用新線程的函數(shù)的呢昙篙?這個(gè)下篇再說(shuō)。