cocos2d-x 3.1中addImageAsync异步加载图片的陷阱

来源:互联网 发布:openstack用哪些数据库 编辑:程序博客网 时间:2024/05/21 07:02


就以往的经验,异步加载图片是一个复杂的工作,往往容易出现bug。

那么,cocos2d-x提供的这个异步加载功能是否可靠?百度了一下,没发现什么重要的信息,于是自己分析之。

照cocos2d-x自身的注释来看,这个addImageAsync函数是从0.8版本就有了,而现在是3.1版本,怎么也该稳定了吧?可惜的是,里面的陷阱并不少。


陷阱1:_textures未加锁

在异步加载时,cocos2d-x主要用了两个队列,即_asyncStructQueue和_imageInfoQueue,在操作这两个队列的时候也都很小心的加锁了。但是对_textures的访问则没有加锁。因此,如果先用addImageAsync进行异步加载图片A,再用addImage同步加载图片B,则有几率导致_textures这个对象被损坏,进而导致程序不稳定。

修改:由于涉及到_textures的地方很多,逐一加锁实在麻烦,所以干脆不加锁,转为让异步线程不要访问_textures。大不了就是同一张图片被多次加载,浪费一些运算量罢了。上层代码小心控制的话,是不会真的有图片被重复加载的。


陷阱2:判断逻辑错误

在cocos2d-x 3.1.1版本中,异步加载的代码中有一句判断:if(imageInfo->asyncStruct->filename.compare(asyncStruct->filename)),这是有问题的。

作者的本意可能是想,如果队列中有多个请求都是加载同一幅图片,那么其实只需要加载一次即可。可惜这个判断写反了,下文又有一处判断写反,导致不知所云。

这个问题在cocos2d-x 3.2版本已经修复了。

P.S. 字符串比较,还是用==、!=这样的操作符比较好,可读性和运行性能都要优于compare函数。

修改:cocos官方已经修正。不过其实跟陷阱1类似,不必判断,大不了就是同一张图片被多次加载,浪费一些运算量罢了。


陷阱3:insert失败导致内存泄漏

在异步加载完毕之后,主线程有一句:_textures.insert( std::make_pair(filename, texture) );。

由于陷阱1、陷阱2中,我们并没有进行彻底的检查,所以有几率出现重复加载的情形。(实际上,除非全程加锁,否则很难彻底避免重复加载。然而,全程加锁的话,异步加载也就没有意义了)。当出现重复加载同一张图片时,这里的insert就会失败。于是,texture不会有被销毁的机会,于是造成内存泄漏。


陷阱4:创建线程时,需要的变量尚未初始化完毕

创建异步加载的线程时,原始代码是先创建线程,再设置_needQuit。

正常应该是先设置_needQuit为false(初始化值为true!),再创建线程。否则理论上有可能线程刚创建完毕就立即结束。


疑似陷阱

异步加载线程和主线程,都调用了Image::initWithImageFileThreadSafe,这个函数看名字似乎是线程安全的,实际上它调用了FileUtils::fullPathForFilename。这个函数除非参数是绝对路径,否则就会对一个名为_fullPathCache的哈希表进行读写,若不加锁就会出错。幸好在异步加载线程中,传入给FileUtils::fullPathForFilename的参数已经是绝对路径,所以没有上述的问题。


总结

这陷阱的数量和浅显程度,让我有一种想要重新写一套异步加载机制的冲动了。然则,暂时还是以修改为主,以重写为辅吧。上述的那些内容都修改掉,多多测试,看看是否有其它问题。

总之,cocos2d-x虽然流行,但是稳定性和质量真的还很值得思量。使用时需要多多小心。

0 0
原创粉丝点击