OGRE多线程的使用

上次说了多线程管理的设计方案,这里再整理下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);

}

你可能感兴趣的:(多线程)