这个系列我们主要学习Cocos2d-x Lua,总结Lua开发过程中所涉及的知识点,以及在开发过程中如何使用Cocos Code IDE。这一篇讲解Lua 异步任务工具类。

思路

实现思路

1.使用pthread库,封装一个用于执行异步任务的类,该类提供一个方法接受一个lua function,然后在子线程中执行该function。

2.使用tolua++工具把C++自定义类绑定到Lua。


代码思路(伪代码)

定义一个C++类AsynTaskHandler用于处理Lua中需要执行的异步任务,这个类中有一个队列(先进先出)用于存放任务。

Task task = NULL;
//在子线程中启动一个循环去处理队列中的任务
while(true){
    //定义一个bool变量标记是否退出循环,在循环开始时判断是否需要退出循环
    if(need_quit){
break;
    }
    task = NULL;
    //从队列中获取任务
    task = task_queue.pop()
    
    //如果队列为空
    if(NULL == task){
        //当前线程进行睡眠状态,等待主线程添加任务并唤醒当前线程
thread_sleep();
//当线程被唤醒,继续当前循环
        continue;
    }
    
    //如果成功获取到任务则执行任务
    doTask(task);
    task.release();    
}
//如果退出了循环
//清理任务队列和资源
if(task_queue){
    task_queue.clear();
    task_queue = NULL;
}
//退出线程
thread_exit();
//初始化
void AsynTaskHandler::lazyInit()
{
    //如果任务队列未初始化
if (taskQueue == NULL)
{
//创建任务队列
        taskQueue = queue.create();
        
// 创建线程
thread_create();
// 执行线程
thead_execute();
        // 初始化退出标记为flase
need_quit = false;
}
}
// 添加任务
void AsynTaskHandler::addTask(int task)
{
    // 初始化
lazyInit();
// 添加到任务队列
    task_queue.add(task);
// 唤醒工作线程
thread_wakeup();
}


实现

#ifndef __ASYNTASKHANDLER_H__
#define __ASYNTASKHANDLER_H__
#include "cocos2d.h"
USING_NS_CC;
// Lua中执行异步任务的工具类
class AsynTaskHandler: public CCObject
{
    public:
        AsynTaskHandler();
        virtual ~AsynTaskHandler();
        void addTask(int task);
        // 懒加载
        void lazyInit();
        /** Return the shared instance **/
        static AsynTaskHandler *getInstance();
        /** Relase the shared instance **/
        static void destroy();
};
#endif  // __ASYNTASKHANDLER_H__


绑定到Lua

绑定到Lua时需要使用的pkg文件,代码如下:

class AsynTaskHandler: public CCObject  
{  
    AsynTaskHandler();  
    ~AsynTaskHandler();  
    void addTask(LUA_FUNCTION task);      
    static AsynTaskHandler *getInstance();  
    static void destroy();  
};

参考文章:使用tolua++工具在Lua中使用C++自定义类

Cocos Code IDE工程中使用的代码提示文件,放在源码目录下:

--------异步任务工具类
--------------------------------
-- @module AsynTaskHandler
--------------------------------
-- @function [parent=#AsynTaskHandler] addTask 
-- @param self
-- @param #int task
--------------------------------
-- @function [parent=#AsynTaskHandler] destory 
-- @param self
--------------------------------
-- @function [parent=#AsynTaskHandler] getInstance 
-- @param self
-- @return AsynTaskHandler#AsynTaskHandler ret (return value: AsynTaskHandler)
return nil


测试

测试代码如下(Lua代码):

-- 测试异步执行任务  
AsynTaskHandler:getInstance():addTask(function()  
    cclog("asyn task is running..")  
    for i=1, 30 do  
        cclog("child thread num=%s",i)     
    end  
end)  
for i=1, 30 do  
    cclog("main thread num=%s",i)     
end

 

日志输出如下:

20141202095650748.jpg

日志中可以看出“main thread num=1”被先输出了,也就是说主线程并没有被阻塞。


注意:在子线程中不能调用Cocos2d-x相关api(比如CCTextureCache),这是由于Cocos2d-x的内存管理机制所限制的。


官方说明如下:

1.Don’t call any functions which invokes Ref::retain(), Ref::release() or Ref::autorelease(), because AutoreleasePool are not thread-safe. Please refer to Reference Count and AutoReleasePool in Cocos2d-x for more details. Cocos2d-x use AutoreleasePool every where in its framework, so my suggestion is that, don’t invoke any cocos2d-x API in a new thread except Data Structures.

AutoreleasePool不是线程安全的。

2.If you want to load resources in a new thread, you can use TextureCache::addImageAsync()

如果你想异步加载资源,可以使用TextureCache::addImageAsync()。

3.pthread_cond_wait() seems have a bug, it can not wait at the first time, but works properly in subsequence.

pthread_cond_wait()有一个bug。它在第一次调用的时候不正常,但后继的使用能正常工作。

关于线程间通信

  • 在Cocos2d-x 2.x中可以使用CCNotificationCenter进行线程间通信(注意:CCNotificationCenter并非线程安全的)。

  • 在Cocos2d-x 3.x中可以使用EventCustomeListener进行线程间通信。


项目git地址:https://coding.net/linchaolong/LuaAsynTaskHandler.git

<