cocos2d-x引擎在内部实现了一个庞大的主循环,每帧之间更新界面,如果耗时的操作放到了主线程中,游戏的界面就会卡,这是不能容忍的,游戏最基本的条件就是流畅性,这就是为什么游戏开发选择C++的原因。另外现在双核手机和四核手机越来越普遍了,是时候使用多线程来挖掘硬件的潜力了。

1.环境搭建

cocos2d-x中的多线程使用pthread就可以实现跨平台,而且也不是很难理解。使用pthread需要先配置一下工程。右击工程----->属性----->配置属性---->链接器----->输入---->附加依赖项中添加pthreadVCE2.lib,如下图

接着添加附加包含目录,右击项目,属性----->C/C++---->常规----->附加包含目录加入pthread头文件所在的目录

这样,环境就搭建起来了。

2.多线程的使用

使用pthread来实现多线程,最重要的一个函数是

PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid,//线程的标示   const pthread_attr_t * attr,      //创建线程的参数   void *(*start) (void *),          //入口函数的指针   void *arg);                       //传递给线程的数据  

在HelloWorldScene.h文件中

pthread_t pidrun,pidgo;
static void* th_run(void *r);
static void* th_go(void *r);  

定义了两个函数和两个线程的标识。

然后自定义了一个类,用于给线程传递数据。Student类如下:

#pragma once
#include <string>
class Student
{
public:  Student(void);  Student(std::string name,int age,std::string sex);  ~Student(void);  std::string name;  int age;  std::string sex;  };   

源文件如下

#include "Student.h"
#include "cocos2d.h"  Student::Student(void)
{
}  Student::~Student(void)
{  cocos2d::CCLog("delete data");
}
Student::Student(std::string name,int age,std::string sex)
{  this->name=name;  this->age=age;  this->sex=sex;
}  

在退出菜单的回调函数中启动两个线程:

void HelloWorld::menuCloseCallback(CCObject* pSender)
{  Student *temp=new Student(std::string("zhycheng"),23,std::string("male"));  pthread_mutex_init(&mutex,NULL);  pthread_create(&pidrun,NULL,th_run,temp);//启动线程  pthread_create(&pidgo,NULL,th_go,0);  }  

可以看到,将Student的指针传递给了pidrun线程,那么在pidrun线程中获得Student信息如下:

      Student *s=(Student*)(r);
CCLog("name is %s,and age is %d,sex is %s",s->name.c_str(),s->age,s->sex.c_str());
delete s;  

3.线程同步

使用了线程,必然就要考虑到线程同步,不同的线程同时访问资源的话,访问的顺序是不可预知的,会造成不可预知的结果。

这里使用pthread_mutex_t来实现同步,下面我来演示一下使用多线程实现卖票系统。卖票的时候,是由多个窗口同时卖票,这里要做到一张票不要卖出去两次,不要出现有票却无法卖的结果。

在线程函数th_run和th_go中来卖票,票的数量是一个全局变量,每卖出去一张票,就将票的数量减一。其中同步的pthread_mutex_t也是一个全局变量,就用它来实现线程同步。

void* HelloWorld::th_run(void *r)
{  Student *s=(Student*)(r);  CCLog("name is %s,and age is %d,sex is %s",s->name.c_str(),s->age,s->sex.c_str());  delete s;  while(true)  {  pthread_mutex_lock(&mutex);  if(ticket>0)  {  CCLog("thread run sell %d",ticket);  ticket--;  pthread_mutex_unlock(&mutex);  }  else  {  pthread_mutex_unlock(&mutex);  break;    }  Sleep(1);  //Usleep(10);
    }  return NULL;
}  

void* HelloWorld::th_go(void *r)
{  while(true)  {  pthread_mutex_lock(&mutex);  if(ticket>0)  {  CCLog("thread go sell %d",ticket);  ticket--;  pthread_mutex_unlock(&mutex);  }  else  {  pthread_mutex_unlock(&mutex);  break;  }  Sleep(1);  }  return NULL;
}  

mutex被锁定后,其他线程若再想锁定mutex的话,必须等待,当该线程释放了mutex之后,其他线程才能锁定mutex。Sleep()函数可以使得该线程休眠,单位是毫秒。

4.注意

1.Sleep()函数是使得线程休眠的函数,这个函数不跨平台,仅仅在windows上能用,其他平台使用usleep。

2.在非主线程中不能使用cocos2d-x管理内存的CCObject::retain()CCObject::release() 者CCObject::autorelease(),因为CCAutoreleasePool不是线程安全的,OPENGL的上下文也不是线程安全的,所以不要再非主线程中使用cocos2d-x的API和UI操作。

 

cocos2dx内存管理与多线程问题:

Cocos2d-x的内存管理采用Objective-C的机制,大喜过望。因为只要坚持Objective-C的原则“谁创建谁释放,谁备份谁释放”的原则即可确保内存使用不易出现Bug。
但是因为本身开放的游戏需要使用到多线程技术,导致测试的时候总是莫名其妙的导致空指针错误。而且是随机出现,纠结了2天无果后,开始怀疑Cocos2d-X的内存本身管理可能存在问题。怀着这样的想法,
一步一步的调试,发现经常出现指针异常的变量总是在调用autorelease后一会就莫名其妙再使用的时候就抛异常。狠下心,在它的析构函数里面断点+Log输出信息。发现对象被释放了。一时也很迷糊,因为对象只是
autorelease,并没有真正释放,是谁导致它释放的?

然后就去看了CCAutoreleasePool的源码,发现存在Cocos2d-X的内存管理在多线程的情况下存在如下问题

如图:thread 1和thread 2是独立的两个线程,它们之间存在CPU分配的交叉集,我们在time 1的时候push一个autorelease的自动释放池,在该线程的末尾,即time 3的时候pop它。同理在thread 2的线程里面,在time 2的时候push一个自动释放池,在time 4的时候释放它,即Pop.
此时我们假设在thread 2分配得到CPU的时候有一个对象obj自动释放,即obj-autorelease().那么在time 3的时候会发生是么事情呢?
答案很简单,就是obj在time 3的时候就被释放了,而我们期望它在time 4的时候才释放。所以就导致我上面说的,在多线程下面,cocos2d-x的autorelease变量会发生莫名其妙的指针异常。

解决办法:在PoolManager给每个线程根据pthread_t的线程id生成一个CCArray的stack的嵌套管理自动释放池。源码如下
所以我在Push的时候根据当前线程的pthread_t的线程id生成一个CCArray的stack来存储该线程对应的Autoreleasepool的嵌套对象
源码如下

//--------------------------------------------------------------------
//
// CCPoolManager
//
//--------------------------------------------------------------------/////【diff - begin】- by layne//

CCPoolManager* CCPoolManager::sharedPoolManager()
{if (s_pPoolManager == NULL){s_pPoolManager = new CCPoolManager();}return s_pPoolManager;
}void CCPoolManager::purgePoolManager()
{CC_SAFE_DELETE(s_pPoolManager);
}CCPoolManager::CCPoolManager()
{//    m_pReleasePoolStack = new CCArray();   //    m_pReleasePoolStack->init();//    m_pCurReleasePool = 0;
m_pReleasePoolMultiStack = new CCDictionary();
}CCPoolManager::~CCPoolManager()
{//    finalize();//    // we only release the last autorelease pool here//    m_pCurReleasePool = 0;//    m_pReleasePoolStack->removeObjectAtIndex(0);//   //    CC_SAFE_DELETE(m_pReleasePoolStack);
finalize();CC_SAFE_DELETE(m_pReleasePoolMultiStack);
}void CCPoolManager::finalize()
{if(m_pReleasePoolMultiStack->count() > 0){//CCAutoreleasePool* pReleasePool;CCObject* pkey = NULL;CCARRAY_FOREACH(m_pReleasePoolMultiStack->allKeys(), pkey){if(!pkey)break;CCInteger *key = (CCInteger*)pkey;CCArray *poolStack = (CCArray *)m_pReleasePoolMultiStack->objectForKey(key->getValue());CCObject* pObj = NULL;CCARRAY_FOREACH(poolStack, pObj){if(!pObj)break;CCAutoreleasePool* pPool = (CCAutoreleasePool*)pObj;pPool->clear();}}}
}void CCPoolManager::push()
{//    CCAutoreleasePool* pPool = new CCAutoreleasePool();       //ref = 1//    m_pCurReleasePool = pPool;//   //    m_pReleasePoolStack->addObject(pPool);                   //ref = 2//   //    pPool->release();                                       //ref = 1
pthread_mutex_lock(&m_mutex);CCArray* pCurReleasePoolStack = getCurReleasePoolStack();CCAutoreleasePool* pPool = new CCAutoreleasePool();         //ref = 1pCurReleasePoolStack->addObject(pPool);                               //ref = 2pPool->release();                                           //ref = 1
pthread_mutex_unlock(&m_mutex);
}void CCPoolManager::pop()
{//    if (! m_pCurReleasePool)//    {//        return;//    }//   //    int nCount = m_pReleasePoolStack->count();//   //    m_pCurReleasePool->clear();//   //    if(nCount > 1)//    {//        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);//        //        //         if(nCount > 1)//        //         {//        //             m_pCurReleasePool = m_pReleasePoolStack->objectAtIndex(nCount - 2);//        //             return;//        //         }//        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);//    }//   //    /*m_pCurReleasePool = NULL;*/
pthread_mutex_lock(&m_mutex);   CCArray* pCurReleasePoolStack = getCurReleasePoolStack();CCAutoreleasePool* pCurReleasePool = getCurReleasePool();   if (pCurReleasePoolStack && pCurReleasePool){int nCount = pCurReleasePoolStack->count();pCurReleasePool->clear();if(nCount > 1){pCurReleasePoolStack->removeObject(pCurReleasePool);}}pthread_mutex_unlock(&m_mutex);
}void CCPoolManager::removeObject(CCObject* pObject)
{//    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");//   //    m_pCurReleasePool->removeObject(pObject);
pthread_mutex_lock(&m_mutex);CCAutoreleasePool* pCurReleasePool = getCurReleasePool();CCAssert(pCurReleasePool, "current auto release pool should not be null");pCurReleasePool->removeObject(pObject);pthread_mutex_unlock(&m_mutex);
}void CCPoolManager::addObject(CCObject* pObject)
{//    getCurReleasePool()->addObject(pObject);
pthread_mutex_lock(&m_mutex);   CCAutoreleasePool* pCurReleasePool = getCurReleasePool(true);CCAssert(pCurReleasePool, "current auto release pool should not be null");pCurReleasePool->addObject(pObject);pthread_mutex_unlock(&m_mutex);
}CCArray* CCPoolManager::getCurReleasePoolStack()
{CCArray* pPoolStack = NULL;pthread_t tid = pthread_self();if(m_pReleasePoolMultiStack->count() > 0){pPoolStack = (CCArray*)m_pReleasePoolMultiStack->objectForKey((int)tid);}if (!pPoolStack) {pPoolStack = new CCArray();m_pReleasePoolMultiStack->setObject(pPoolStack, (int)tid);pPoolStack->release();}return pPoolStack;
}CCAutoreleasePool* CCPoolManager::getCurReleasePool(bool autoCreate)
{//    if(!m_pCurReleasePool)//    {//        push();//    }//   //    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");//   //    return m_pCurReleasePool;
CCAutoreleasePool* pReleasePool = NULL;CCArray* pPoolStack = getCurReleasePoolStack();if(pPoolStack->count() > 0){pReleasePool = (CCAutoreleasePool*)pPoolStack->lastObject();}if (!pReleasePool && autoCreate) {CCAutoreleasePool* pPool = new CCAutoreleasePool();         //ref = 1pPoolStack->addObject(pPool);                               //ref = 2pPool->release();                                           //ref = 1
pReleasePool = pPool;}return pReleasePool;
}/////【diff - end】- by layne//
代码下载地址:https://github.com/kaitiren/pthread-test-for-cocos2dx

cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题相关推荐

  1. cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题

    cocos2d-x引擎在内部实现了一个庞大的主循环,每帧之间更新界面,如果耗时的操作放到了主线程中,游戏的界面就会卡,这是不能容忍的,游戏最基本的条件就是流畅性,这就是为什么游戏开发选择C++的原因. ...

  2. 3、Linux多线程,线程同步(转)

    3.Linux多线程,线程同步 5)线程私有数据 进程内的所有线程共享进程的数据空间,因此全局变量为所有线程所共有.但有时线程也需要保存自己的私有数据,这时可以创建线程私有数据(Thread-spec ...

  3. Java 多线程和线程同步总结

    转载:JAVA多线程实现和线程同步总结 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable ...

  4. VC++ MFC 多线程及线程同步(详细、全面总结!)

    更多详情:http://blog.csdn.net/whyacinth/ VC++ MFC 多线程及线程同步 关键词: MFC    多线程及线程同步                          ...

  5. Java多线程之线程同步机制(锁,线程池等等)

    Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...

  6. C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent

    C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent 本章概要: 1:终止状态和非终止状态 2:AutoResetEvent和ManualResetE ...

  7. MFC 多线程及线程同步

    一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常 ...

  8. 【Java 并发编程】多线程、线程同步、死锁、线程间通信(生产者消费者模型)、可重入锁、线程池

    并发编程(Concurrent Programming) 进程(Process).线程(Thread).线程的串行 多线程 多线程的原理 多线程的优缺点 Java并发编程 默认线程 开启新线程 `Ru ...

  9. java多线程:线程同步synchronized(不同步的问题、队列与锁),死锁的产生和解决

    0.不同步的问题 并发的线程不安全问题: 多个线程同时操作同一个对象,如果控制不好,就会产生问题,叫做线程不安全. 我们来看三个比较经典的案例来说明线程不安全的问题. 0.1 订票问题 例如前面说过的 ...

最新文章

  1. 如何提升微服务的幸福感
  2. python exec
  3. 深度讲解:同步/异步/阻塞/非阻塞/BIO/NIO/apr
  4. 用python实现AES加密解密
  5. wxWidgets:wxStyledTextCtrl类用法
  6. 查询速度提升200倍,ClickHouse到底有多快?
  7. docker安装pocbox(漏洞测试验证辅助平台)
  8. Ubuntu 修改默认的PDF打开方式
  9. 网页不够惊艳?优秀案例给你灵感
  10. Python 在数据科学中一直打压 R 语言?
  11. 在Mac中如何通过命令对NTFS磁盘格式化
  12. mybatis 不生效 参数_Spring Boot(七):你不能不知道的Mybatis缓存机制
  13. orangepi自启动打开一个终端并且运行脚本
  14. web网页设计实例作业 网页Dreamweaver设计
  15. 后台可视化布局打印设计
  16. 多线程与单线程的区别
  17. 农夫过河问题的c语言实现
  18. PVCBOT【27号】机械避役--线控变色龙机器人
  19. MySQL查询日期为一年第几天第几周,天数周数反查日期
  20. passive-interface 总结整理

热门文章

  1. 为什么工作10年你的工资还不如新来的实习生
  2. coreutils-5.0中几个命令的执行过程
  3. 设计原则——依赖倒置
  4. ajax请求,请求头是provisional are shown。请求未发送出去
  5. linux正则表达式awk讲解
  6. Junit指定测试执行顺序
  7. 各种Java日志框架的比较
  8. 在Win7中安装Oracle 10g时检测报错的解决办法
  9. Exchange2003 OWA 将HTTPS转为HTTP访问!〖罗斌个人工作经验谈〗
  10. UpdateProgress学习