思路
实现思路
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();
}
实现
#include "AsynTaskHandler.h"
#include "CCLuaEngine.h"
#include
//#include
// 单例
static AsynTaskHandler *s_pAsynTask = NULL;
// 任务队列
static CCArray* s_taskQueue = NULL;
//static std::vector
s_taskQueue;
// 线程锁
static pthread_mutex_t s_taskQueueMutex;
// 条件变量
static pthread_mutex_t s_SleepMutex;
static pthread_cond_t s_SleepCondition;
//线程id
static pthread_t s_workThread;
// 任务循环标记
static bool need_quit = false;
// 工作线程
static void* workThread(void *data){
CCInteger* task = NULL;
while (true)
{
// 如果接收到退出信号,退出循环
if (need_quit)
{
break;
}
task = NULL;
// 从任务队列中获取在队头的任务
pthread_mutex_lock(&s_taskQueueMutex);
if (0 != s_taskQueue->count())
{
task = dynamic_cast
(s_taskQueue->objectAtIndex(0));
s_taskQueue->removeObjectAtIndex(0);
}
pthread_mutex_unlock(&s_taskQueueMutex);
// 如果队列中没有任务
if (NULL == task)
{
// 线程进行睡眠状态,等待主线程添加任务并唤醒当前线程
pthread_cond_wait(&s_SleepCondition, &s_SleepMutex);
continue;
}
//如果获取任务成功,执行任务
CCLuaStack* pStack = CCLuaEngine::defaultEngine()->getLuaStack();
//第一个参数是函数的整数句柄,第二个参数是函数参数个数
pStack->executeFunctionByHandler(task->getValue(),0);
pStack->clean();
task->release();
}
// 如果接收到退出信号,清理任务队列和释放资源
pthread_mutex_lock(&s_taskQueueMutex);
s_taskQueue->removeAllObjects();
pthread_mutex_unlock(&s_taskQueueMutex);
if (s_taskQueue != NULL) {
pthread_mutex_destroy(&s_taskQueueMutex);
pthread_mutex_destroy(&s_SleepMutex);
pthread_cond_destroy(&s_SleepCondition);
s_taskQueue->release();
s_taskQueue = NULL;
}
pthread_exit(NULL);
return 0;
}
void AsynTaskHandler::lazyInit()
{
if (s_taskQueue == NULL)
{
//创建任务队列
s_taskQueue = new CCArray();
s_taskQueue->init();
// 初始化线程锁
pthread_mutex_init(&s_taskQueueMutex, NULL);
// 初始化条件变量
pthread_mutex_init(&s_SleepMutex, NULL);
pthread_cond_init(&s_SleepCondition, NULL);
// 创建线程
pthread_create(&s_workThread, NULL, workThread, NULL);
// 执行线程
pthread_detach(s_workThread);
need_quit = false;
}
}
AsynTaskHandler::AsynTaskHandler(){}
AsynTaskHandler::~AsynTaskHandler(){
// 停止循环
need_quit = true;
//销毁任务队列
if (s_taskQueue != NULL) {
// 唤醒线程,清理资源
pthread_cond_signal(&s_SleepCondition);
}
s_pAsynTask = NULL;
}
/** Return the shared instance **/
AsynTaskHandler *AsynTaskHandler::getInstance(){
if (s_pAsynTask == NULL) {
s_pAsynTask = new AsynTaskHandler();
}
return s_pAsynTask;
}
/** Relase the shared instance **/
void AsynTaskHandler::destroy(){
s_pAsynTask->release();
}
// 添加任务
void AsynTaskHandler::addTask(int task)
{
lazyInit();
// 添加到任务队列
pthread_mutex_lock(&s_taskQueueMutex);
s_taskQueue->addObject(CCInteger::create(task));
pthread_mutex_unlock(&s_taskQueueMutex);
// 唤醒工作线程
pthread_cond_signal(&s_SleepCondition);
}
#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
点击查看【使用tolua++工具在Lua中使用C++自定义类】
绑定到Lua时需要使用的pkg文件,代码如下:
class AsynTaskHandler: public CCObject
{
AsynTaskHandler();
~AsynTaskHandler();
void addTask(LUA_FUNCTION task);
static AsynTaskHandler *getInstance();
static void destroy();
};
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不是线程安全的。
AutoreleasePool不是线程安全的。
2.If you want to load resources in a new thread, you can use TextureCache::addImageAsync()
如果你想异步加载资源,可以使用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-x2.x中可以使用CCNotificationCenter进行线程间通信(注意:CCNotificationCenter并非线程安全的)。
在Cocos2d-x3.x中可以使用EventCustomeListener进行线程间通信。