How to support multithread in cocos2dx

Usually, an application is separated into two parts: foreground and backgroud, where foreground is charge of drawing UI and doing UI flow controlling, backgroud is responsible for processing data logica. The foreground is named main UI thread and the backgroud layer is usually a worker thread.

But in cocos2dx, all the operations are done in the UI thread via CCDisplayLinkDirector::mainLoop in CCDirector.cpp. To avoid blocking UI thread, we can use the two methods to achieve it:
> Using timer mechanism, i.e., CCScheduler. 
    With this way, the operation will be execute until the timeout is triggered, but it is still run in UI thread. So when a blocking call is invoked, the UI thread is still be blocked.
> Using worker thread to do the blocking operation, such as reading data from network etc. 
    When there is no data comes, the worker thread will be blocked, and it will spend no CPU times. Since it will not never blocking UI thread, it is the prefered method.
    
Cocos2dx doesn't provide an implementation for multithreading, this article provides a way to achieve multithreading in cocos2dx. It will need to change several source files in cocos2dx framework.

The goal of this implementation is that:
> Using pthread which is supported in most of platforms, such as linux, windows, even ios, android etc. (cocos2dx/httpclient is using pthread)
> Easy for any CCNode (also including its subclasses) who wants to handle the messages to register/unregister the handler.
> Provides a message queue for each worker thread for communicating with other threads

The main class design is as below:

    class EXTCCHandler {
    public:
        // Max len of the name is 32 char
        EXTCCHandler(const char *pName);
        virtual ~EXTCCHandler();
        
        // it will suspend current thread if there is no message in queue
        static bool hasMessage(const EXTCCHandler * const handler);
        
        // it will proccess messages in queue and
        // return till all the messages in queue are processed
        static void processMessage(const EXTCCHandler * const handler);
        
        // register a handler to current hander thread
        // when a message comes,
        // "processMessage" will dispatch msg to each registered handler
        void registerHandler(const CCObject *pTarget, const EXTCCHandleMessage &handler);
        
        // unregister a handler
        void unregisterHandler( const CCObject *pTarget,   const EXTCCHandleMessage &handler);
        
        // post a message to current handler thread, it's async call
        void postMessage(const EXTCCMessage &msg);
        
    };
    
    class EXTCCHandlerThread : public EXTCCHandler {
    public:
        ~EXTCCHandlerThread();
        
        // create a handler thread, Max len of the thread name is 32 char
        static EXTCCHandlerThread *create(const char *pThreadName);
            
    protected:
        EXTCCHandlerThread(const char *pThreadName);
        
    private:
        static void *loop(void *pArgs);
    
    };

Some changes in Cocos2dx framework:
> make folder of "HandlerThread" under "cocos2d-x-2.x.x/cocos2dx/platform"
> copy the source code to "cocos2d-x-2.x.x/cocos2dx/platform/HandlerThread"
> find CCDirector.cpp and CCDirector.h (which locate in cocos2dx/) and add static variable in CCDirector.cpp (below "static CCDisplayLinkDirector *s_SharedDirector = NULL;")
     static EXTCCHandler *s_MainLoopHandler = NULL;
 
> add static function in class CCDirector
     CCDirector.h: add below class declaration before  NS_CC_BEGIN :
        class EXTCCHandler;
        
    CCDirector.h: (add below statement blow "static CCDirector* sharedDirector(void);"
        static EXTCCHandler *mainLoopHandler(void);
        
    CCDirector.cpp: (add below implementation below static method "CCDirector* CCDirector::sharedDirector(void)"    
    EXTCCHandler *CCDirector::mainLoopHandler(void) {
        if (s_MainLoopHandler == NULL) {
            s_MainLoopHandler = new EXTCCHandler("MainUILoop");
        }
    
        return s_MainLoopHandler;
    }
    
    CCDirector.cpp: add below statements into CCDisplayLinkDirector::mainLoop function
    void CCDisplayLinkDirector::mainLoop(void)
    {
        if (m_bPurgeDirecotorInNextLoop)
        {
            m_bPurgeDirecotorInNextLoop = false;
            purgeDirector();
        }
        else if (! m_bInvalid)
         {
             drawScene();
         
             // release the objects
             CCPoolManager::sharedPoolManager()->pop();        
         }
        
        /// { added for supporting handler thread ///
        if (s_MainLoopHandler) {
            EXTCCHandler::processMessage(s_MainLoopHandler);
        }
        /// added for supporting handler thread } ///
    }     
        
> add header file includes in "cocos2d-x-2.x.x/cocos2dx/include/cocos2d.h"
    // Handler thread
     #include "platform/HandlerThread/EXTCCHandlerThread.h" 
    
    
In Xcode, we MUST run "install-templates-xcode.sh" again to update the templates in Xcode:
    in console, run below command:
     sudo ./install-templates-xcode.sh -f
NOTE: because we didn't udpate the templates project file in cocos2dx templates for Xcode,  so on every time we create Xcode cocos2dx project, we MUST copy the folder of "HandlerThread" into the created project folder (xxx is the created project name): "xxx//libs/cocos2dx/platform/", and add these files in XCode project explorer manually.

In Android, we MUST update Android.mk in "cocos2d-x-2.x.x/cocos2dx", add below source files in LOCAL_SRC_FILES:
     \
    platform/HandlerThread/EXTCCHandler.cpp \
    platform/HandlerThread/EXTCCHandlerThread.cpp

Only XCode and Android platform I've tried, if on other platforms, you must do similar changes by yourself.

How to use:
0. In UI thread, we can register a handler to process message in UI thread.
    bool HelloWorldScene::handleMessageInUIThread(const EXTCCMessage &msg) {
        // implementations
        // return true if this handler has processed this msg, otherwise, false is returned
        switch (msg.msgId) {
        case 2:
            break;
            
        default:
            return false;    
        }
        
        return true;
    }
    
    // register this Handler to UI Threader
    CCDirector::mainLoopHandler()->registerHandler(this, 
                     (EXTCCHandleMessage)&
HelloWorldScene:: handleMessageInUIThread);
    
1. Creating worker thread:
     EXTCCHandlerThread *g_workThread = EXTCCHandlerThread::create("WorkThreadName");
    
2. Register a message handle to this created handler thread (function of handleMessage will be run in g_workThread):
    bool  HelloWorldScene:: handleMessageInWorkerThread(const EXTCCMessage &msg) {
        // implementations
        // return true if this handler has processed this msg, otherwise, false is returned
        switch (msg.msgId) {
        case 1:
            break;
            
        default:
            return false;    
        }
        
        return true;
    }
    
    g_workThread->registerHandler(this,
                   
(EXTCCHandleMessage)& HelloWorldScene:: handleMessageInWorkerThread);

3. When we get a handlerThread handler, we can post message in UI thread or other worker thread to it for processing:
     EXTCCMessage msg;
    msg.msgId = 1;
    msg.msgData1 = NULL;
    
// "msg" will be processed by "handleMessageInWorkerThread" running in g_workThread  
    g_workThread->postMessage(msg); 

4. In a worker thread, we can send message to UI thread for updating UI:
     EXTCCMessage msg;
    msg.msgId = 2;
    msg.msgData1 = NULL;
    
// "msg" will be processed by "handleMessageInUIThread" in UI thread  
    CCDirector::mainLoopHandler()->postMessage(msg); 
    
5. If the object doesn't need to proccess message anymore, you MUST unregister the handler to avoid memory leak:
     CCDirector::mainLoopHandler()->unregisterHandler(this, 
                   
(EXTCCHandleMessage)& HelloWorldScene:: handleMessageInUIThread);
    g_workThread->unregisterHandler(this, 
                   
(EXTCCHandleMessage)& HelloWorldScene:: handleMessageInWorkerThread);      

Notice in using multithreads in cocos2dx:
> In worker thread, it CAN NOT update UI element at any time. The corrected way is posting a message which contains the updating data to UI thread for doing updates. (refer to:  http://www.cocos2d-x.org/projects/cocos2d-x/wiki/How_to_use_pthread#How-to-use-pthread)
> This handler thread doesn't contains Autorelease mechanism provided by Cocos2dx, so  it's user's responsibility to do release on msgData in EXTCCMessage.
> There is no limits on HanderThread's message queue. It will grow double length (the default length is 16) if there is no slot for new coming message
> Because Android didn't provide a cancel call for pthread (pthread_cancel, pthread_testcancel), so in this implementation, no EXTCCHandlerThread::cancel is provided. If you want this API, pls google it, there is some ways to achieve it.
> If a message didn't be processed by any handler, a log will be output:
    ###Message [msgId] is NOT processed by thread [ThreadName]!!!

Source code: 
Right click below picture to save it to your PC, rename suffix name to rar, and open it with winRar:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值