上次说了多线程管理的设计方案,这里再整理下OGRE中多线程的使用方法,由于新的WorkQueue设计是通用型的,因此对于我们来说,只需要使用好这个结构即可方便地实现多线程功能。
1、继承WorkQueue::RequestHandler、WorkQueue::ResponseHandler,准备一个uint16 mWorkQueueChannel,在类的构造函数中通过以下代码初始化:
WorkQueue* wq = Root::getSingleton().getWorkQueue();
mWorkQueueChannel = wq->getChannel("Ogre/Terrain"); // 这个名字自己取好
wq->addRequestHandler(mWorkQueueChannel, this);
wq->addResponseHandler(mWorkQueueChannel, this);
析构时移除:
waitForDerivedProcesses(); // 这个最后说明
WorkQueue* wq = Root::getSingleton().getWorkQueue();
wq->removeRequestHandler(mWorkQueueChannel, this);
wq->removeResponseHandler(mWorkQueueChannel, this);
_____________________________________________________________________________________________________________________________________________________
2、准备两个结构体,用于记录Request/Response的Contex,Request与Response容易看错,注意颜色区分
/// A data holder for communicating with the background derived data update
struct DerivedDataRequest
{
Terrain* terrain;
_OgreTerrainExport friend std::ostream& operator<<(std::ostream& o, const DerivedDataRequest& r)
{ return o; } // 这个函数要导出
};
/// A data holder for communicating with the background derived data update
struct DerivedDataResponse
{
Terrain* terrain;
PixelBox* lightMapBox;
_OgreTerrainExport friend std::ostream& operator<<(std::ostream& o, const DerivedDataResponse& r)
{ return o; } // 这个函数要导出
};
_____________________________________________________________________________________________________________________________________________________
3、重载以下4个函数
/// WorkQueue::RequestHandler override
bool canHandleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ);
/// WorkQueue::RequestHandler override
WorkQueue::Response* handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ);
/// WorkQueue::ResponseHandler override
bool canHandleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ);
/// WorkQueue::ResponseHandler override
void handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ);
其中handleRequest是在多线程中处理实际加载数据的函数,具体的读取、创建之类的操作都在此完成。
对于 canHandleRequest、canHandleResponse基本上就是判定是不是自己的处理对象,一般可以不管,但为了谨慎起见还是需要对请求任务判定,以免多个同类任务执行时会有冲突:
bool Terrain::canHandleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ)
{
DerivedDataRequestddr = any_cast<DerivedDataRequest>(req->getData());
// only deal with own requests
// we do this because if we delete a terrain we want any pending tasks to be discarded
if (ddr.terrain != this)
return false;
else
return RequestHandler::canHandleRequest(req, srcQ);
}
//---------------------------------------------------------------------
bool Terrain::canHandleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
{
DerivedDataRequestddreq = any_cast<DerivedDataRequest>(res->getRequest()->getData());
// only deal with own requests
// we do this because if we delete a terrain we want any pending tasks to be discarded
if (ddreq.terrain != this)
return false;
else
return true;
}
上面就是要注意用any转型时取得的源对象是什么。(res->getRequest()->getData() 与 req->getData())
对于handleRequest最后要做的就是返回一个新的处理结果:
WorkQueue::Response* Terrain::handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ)
{
DerivedDataResponse ddres;
把操作的结果都放到ddres中返回给主线程中去处理
WorkQueue::Response* response = OGRE_NEW WorkQueue::Response(req, true, Any(ddres));
return response;
}
_____________________________________________________________________________________________________________________________________________________
4、添加具体的任务请求(部分代码省略)
DerivedDataRequest req;
req.terrain = this;
Root::getSingleton().getWorkQueue()->addRequest(
mWorkQueueChannel, WORKQUEUE_DERIVED_DATA_REQUEST,
Any(req), 0, synchronous);
其中 :const uint16 Terrain::WORKQUEUE_DERIVED_DATA_REQUEST = 1,对于同个对象,如果有不同的数据操作类型,可以用多个不同的ID区别。
_____________________________________________________________________________________________________________________________________________________
注意:在析构时可能还有请求的任务没完成,因此要等待任务完成后才能移除,要不会CRASH。这个需要自己维护一个标记,并让线程队列处理。基本上是在addRequest的时候设置为true,在handleResponse时设置为false:
bool mDerivedDataUpdateInProgress;
void Terrain::waitForDerivedProcesses()
{
while (mDerivedDataUpdateInProgress)
{
// we need to wait for this to finish
OGRE_THREAD_SLEEP(50);
Root::getSingleton().getWorkQueue()->processResponses();
}
}
void Terrain::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
{
mDerivedDataUpdateInProgress = false;
}
void Terrain::updateDerivedDataImpl(const Rect& rect, const Rect& lightmapExtraRect,
bool synchronous, uint8 typeMask)
{
mDerivedDataUpdateInProgress = true;
Root::getSingleton().getWorkQueue()->addRequest(
mWorkQueueChannel, WORKQUEUE_DERIVED_DATA_REQUEST,
Any(req), 0, synchronous);
}