这个系列我们主要学习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
日志输出如下:
日志中可以看出“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
<