学习Cocos2d-x Lua:异步任务工具类

2015年03月24日 10:51 0 点赞 0 评论 更新于 2017-05-09 20:52

在这个系列中,我们将深入学习Cocos2d-x Lua,总结Lua开发过程中涉及的知识点,以及如何在开发过程中使用Cocos Code IDE。本篇文章将详细讲解Lua异步任务工具类。

思路

实现思路

  1. 使用pthread库封装异步任务类:创建一个用于执行异步任务的类,该类提供一个方法,该方法接受一个Lua函数,然后在子线程中执行这个函数。
  2. 使用tolua++工具进行绑定:把C++自定义类绑定到Lua,以便在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();
// 执行线程
thread_execute();
// 初始化退出标记为false
need_quit = false;
}
}

// 添加任务
void AsynTaskHandler::addTask(int task) {
// 初始化
lazyInit();
// 添加到任务队列
task_queue.add(task);
// 唤醒工作线程
thread_wakeup();
}

实现

以下是AsynTaskHandler类的头文件定义:

#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. 内存管理限制:不要调用任何会调用Ref::retain()Ref::release()Ref::autorelease()的函数,因为AutoreleasePool不是线程安全的。更多详细信息请参考Cocos2d-x中的引用计数和自动释放池。Cocos2d-x在其框架中到处都使用了AutoreleasePool,因此建议除了数据结构外,不要在新线程中调用任何Cocos2d-x API。
  2. 异步加载资源:如果你想在新线程中加载资源,可以使用TextureCache::addImageAsync()
  3. pthread_cond_wait()的问题pthread_cond_wait()似乎有一个bug,它第一次调用时不能正常等待,但后续调用可以正常工作。

项目地址

项目git地址

作者信息

boke

boke

共发布了 1025 篇文章