第二人生里使用线程循环来处理消息,这样的结构就比较清晰。比如有一个写文件的请求,就可以把这个请求放到线程队列里,然后唤醒线程,让线程处理这个请求。那么在第二人生里是怎么样构造消息循环呢?又是怎么样执行其它线程发过来的请求呢?带着这两个问题来分析下面这几段代码。
#001 void LLQueuedThread::run()
#002 {
下面实现消息循环。
#003
while (1)
#004
{
#005
// this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
下面检查是否暂停线程执行。
#006
checkPause();
#007
检查线程是否要结束循环。
#008
if(isQuitting())
#009
break;
#010
#011
//llinfos << "QUEUED THREAD RUNNING, queue size = " << mRequestQueue.size() << llendl;
#012
标志线程已经非空闲状态。
#013
mIdleThread = FALSE;
#014
调用processNextRequest函数来处理本线程消息队列里的消息。
#015
int res = processNextRequest();
#016
if (res == 0)
#017
{
#018
mIdleThread = TRUE;
#019
}
#020
处理线程消息出错,退出线程循环。
#021
if (res < 0) // finished working and want to exit
#022
{
#023
break;
#024
}
#025
#026
//LLThread::yield(); // thread should yield after each request
#027
}
#028
#029
llinfos << "QUEUED THREAD " << mName << " EXITING." << llendl;
#030 }
由线程接口类LLThread就知道,线程里主要处理函数是run(),而在LLThread继承类LLQueuedThread里也就是实现了这个函数的功能,主要就是调用函数processNextRequest来处理消息队列里的消息。下面就来分析函数processNextRequest的代码,如下:
#001
#002 S32 LLQueuedThread::processNextRequest()
#003 {
#004
QueuedRequest *req;
#005
// Get next request from pool
锁住线程消息队列。
#006
lockData();
循环地找到可用的消息。
#007
while(1)
#008
{
#009
req = NULL;
线程消息队列为空,退出处理消息。
#010
if (mRequestQueue.empty())
#011
{
#012
break;
#013
}
获取第一个消息请求。
#014
req = *mRequestQueue.begin();
删除第一个消息请求。
#015
mRequestQueue.erase(mRequestQueue.begin());
判断是否丢掉这个消息请求。
#016
if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
#017
{
#018
req->setStatus(STATUS_ABORTED);
#019
req->finishRequest(false);
#020
if (req->getFlags() & FLAG_AUTO_COMPLETE)
#021
{
#022
mRequestHash.erase(req);
#023
req->deleteRequest();
#024 //
check();
#025
}
#026
continue;
#027
}
#028
llassert_always(req->getStatus() == STATUS_QUEUED);
#029
break;
#030
}
设置这个消息正在处理过程中。
#031
if (req)
#032
{
#033
req->setStatus(STATUS_INPROGRESS);
#034
}
解锁消息队列。
#035
unlockData();
#036
#037
// This is the only place we will call req->setStatus() after
#038
// it has initially been seet to STATUS_QUEUED, so it is
#039
// safe to access req.
下面开始处理获取到的消息。
#040
if (req)
#041
{
开始调用这个消息的特别处理函数。
#042
// process request
#043
bool complete = req->processRequest();
#044
判断这个请求是否完成。
#045
if (complete)
#046
{
#047
lockData();
#048
req->setStatus(STATUS_COMPLETE);
#049
req->finishRequest(true);
#050
if (req->getFlags() & FLAG_AUTO_COMPLETE)
#051
{
#052
mRequestHash.erase(req);
#053
req->deleteRequest();
#054 //
check();
#055
}
#056
unlockData();
#057
}
#058
else
#059
{
#060
lockData();
#061
req->setStatus(STATUS_QUEUED);
#062
mRequestQueue.insert(req);
#063
U32 priority = req->getPriority();
#064
unlockData();
#065
if (priority < PRIORITY_NORMAL)
#066
{
#067
ms_sleep(1); // sleep the thread a little
#068
}
#069
}
#070
}
#071
查看是否需要退出线程。
#072
S32 res;
#073
S32 pending = getPending();
#074
if (pending == 0)
#075
{
#076
if (isQuitting())
#077
{
#078
res = -1; // exit thread
#079
}
#080
else
#081
{
#082
res = 0;
#083
}
#084
}
#085
else
#086
{
#087
res = pending;
#088
}
#089
return res;
#090 }
#091
通过在processNextRequest里调用更加具体的消息处理函数processRequest来实现各个消息处理,由于processRequest也是纯虚函数,因此通过实现这个函数不同的形式,就可以实现不同的内容处理,这就是C++的多态特性。不过,要注意的是这个函数可能由多线程访问,需要进行同步的操作。