关于多线程数据共享的问题

  • 本帖最后由 lonely_wm 于 2012-6-14 08:33 编辑 *

在制作的过程中,我将资源数据加载到一个单例工厂中,用CCMultipleArray封装了一些frame和animation,在主线程中加载这些资源都没有问题,问题出在在standtouch相关的touch操作中 和 this->schedule(schedule_selector(testSingle::updates));类似的update函数中,很显然cocos2d-x采用了多线程,在这样的函数中如果声明一个frame或者animation加载(引用)单例模式中的相应的frame或animation就会出错。而且就算将这些资源设置为全局静态变量或者是在前面加上volatile也没有用(经过测试的)。请问哪位大神知道如何从多个线程共享数据的角度解决这个问题,或者从其他方面入手也可以,小弟不甚感激。

附上测试代码,大家也可以帮忙测试一下:
一、在helloWorld.h加上

class testSingle:public CCSprite
{
public:
void load();
void updates(ccTime mTime);
bool isAdded;
};

class Factory
{
public:
CCArray* arrays;
void init();
static Factory * sharedFactory();
};

二、在helloWorld.cpp中加上:
static Factory factory;
static bool isFirst=true;
void testSingle::load()
{
CCSprite::init();
isAdded=false;
this->schedule(schedule_selector(testSingle::updates));
}
void testSingle::updates(ccTime mTime)
{
//确保只运行一次而不是没一帧都调用;
if (!isAdded)
{
Factory fac=Factory::sharedFactory();
CCArray arrayTmp=fac->arrays;
CCSprite
spt=(CCSprite
)arrayTmp->objectAtIndex(0);
HelloWorld helloworld=(HelloWorld)getParent();
helloworld->addChild(spt);
spt->setPosition(ccp(100,100));
isAdded=true;
}
}
static Factory factory;
static bool isFirst=true;
Factory* Factory::sharedFactory()
{
if (isFirst)
{
isFirst=false;
factory.init();
}

    return &factory;

}
void Factory::init()
{
arrays=CCArray::arrayWithCapacity(10);
CCSprite *sprt=CCSprite::spriteWithFile(“HelloWorld.png”);
arrays->addObject(sprt);
}

另外在bool HelloWorld::init()函数的bRet = true;前面加上:
//注意先加载一次fac,让起在主线程中初始化(这个仅在第一次调用的时候初始化),以免后面在update函数中初始化;
//如果将这一句去掉,单例factory将会在子线程中初始化,程序将正常运行,但是此时无法再主线程调用sharedfactory;
Factory* fac=Factory::sharedFactory();
testSingle* te=new testSingle;
te->load();
addChild(te);

大家有兴趣可以测试一下,注意我写的注释。

自己顶一下吧,现在问题已经解决:
解决过程:

查阅了大量资料在http://www.cocos2d-x.org/projects/cocos2d-x/wiki/How_to_use_pthread中找到了cocos2d-x是线程不安全的,若在子线程中初始化texture最好用CCTextureCache::addImageAsync().后来发现这样也很麻烦还要改代码,于是我又进行了很多尝试,无意中发现用CCMultipleArray封装变量在子线程是可以被正常访问的(之前的Array出现了无法预知的错误,所以建议大家用强类型的CCMultipleArray<>),于是问题就这样解决了。我想也许是cocos2d-x自身的内存管理问题,因为本人对c++吃的也不是很透,对vector如何内存进行管理没有研究过,另外在官方的论坛里看到很多CCLabelTTF在子线程中改变数字会出现空白的现象我想估计也是自身内存管理的问题,当然作者也说了会在下一个版本对多线程的内存管理进行改进。
不知道有没有仁兄跟我遇到相同的问题?

CCLabelTTF, CCSprite等用到OpenGL ES函数的地方,由于跨线程调用会导致OpenGL内部状态机乱掉,所以cocos2d core api部分都不能跨线程。

CCAutoreleasePool不能跨线程是另外一个话题,由于在mainloop要结束当前循环的时候,CCAutoreleasePool去清理了所有标记为autorelease的CCObject,而清理完后当次UI循环结束,如果此时还有其他线程仍然在调用这个CCObject, 就会拿到一个野指针。

— Begin quote from ____

walzer 发表于 2012-6-16 21:48 url

CCLabelTTF, CCSprite等用到OpenGL ES函数的地方,由于跨线程调用会导致OpenGL内部状态机乱掉,所以 cocos2d …

— End quote

感谢!但是我发现用CCMutableArray是可以返回真是Sprite的,而用Array就不行,这个问题何解?

获取最新版本,CCMutableArray 和CCMultipleArray 均不可以使用,为什么?

经过反复尝试,我发现在多线程中可以共享的数据是全局数据,而不是某个类中的数据,所以如果你的数据需要在多线程中使用的话无论用什么封装(如CCArray),只要是全局变量皆可:
举例说明一下:

ClassOne.cpp中你可以在 using namespace cocos2d;的下一行添加一个你需要的全局共享变量就可以了。如
static ClassOne* _classOneTmp;(反正我是用的都是单例所以加了static但不知道不用可不可以)

  • 本帖最后由 badapple126 于 2012-7-4 22:04 编辑 *

http://cocos2d.cocoachina.com/bbs/forum.php?mod=redirect&goto=findpost&ptid=1262&pid=4915&fromuid=3836
我以前用的就是CCMutableArray 但是2.0取消了。才按照推荐换成了CCArray

按照以上说明,http://www.cocos2d-x.org/projects/cocos2d-x/wiki/Chapter_5_-_How_to_Detect_the_Collisions
这个例子里面的改成了CCAarray,
不过使用的时候,还是依然无法通过,会报内存错误
过程如下,
bool HelloWorld::init()
{
_targets=CCArray::create();
_projectiles=CCArray::create();


this->schedule( schedule_selector(HelloWorld::gameLogic), 1.0 );

void HelloWorld::gameLogic(float dt){
this->addTarget();
}

鉴证时刻
// cpp with cocos2d-x
void HelloWorld::addTarget()
{

target->setTag(1);
_targets->addObject(target);

} 这里必定报错? 为啥

CCLabelTTF, CCSprite等用到OpenGL ES函数的地方,由于跨线程调用会导致OpenGL内部状态机乱掉,所以cocos2d core api部分都不能跨线程。

CCAutoreleasePool不能跨线程是另外一个话题,由于在mainloop要结束当前循环的时候,CCAutoreleasePool去清理了所有标记为autorelease的CCObject,而清理完后当次UI循环结束,如果此时还有其他线程仍然在调用这个CCObject, 就会拿到一个野指针。

获取最新版本,CCMutableArray 和CCMultipleArray 均不可以使用,为什么?

  • 本帖最后由 badapple126 于 2012-7-4 22:04 编辑 *

http://cocos2d.cocoachina.com/bbs/forum.php?mod=redirect&goto=findpost&ptid=1262&pid=4915&fromuid=3836
我以前用的就是CCMutableArray 但是2.0取消了。才按照推荐换成了CCArray

按照以上说明,http://www.cocos2d-x.org/projects/cocos2d-x/wiki/Chapter_5_-_How_to_Detect_the_Collisions
这个例子里面的改成了CCAarray,
不过使用的时候,还是依然无法通过,会报内存错误
过程如下,
bool HelloWorld::init()
{
_targets=CCArray::create();
_projectiles=CCArray::create();


this->schedule( schedule_selector(HelloWorld::gameLogic), 1.0 );

void HelloWorld::gameLogic(float dt){
this->addTarget();
}

鉴证时刻
// cpp with cocos2d-x
void HelloWorld::addTarget()
{

target->setTag(1);
_targets->addObject(target);

} 这里必定报错? 为啥