cocos2dx-3 addImageAsync陷阱
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
addImageAsync異步加載
未響應(yīng)回調(diào)前調(diào)用unbindImageAsync撤銷消息回調(diào)
void TextureCache::unbindImageAsync(const std::string& filename)
{
? ? _imageInfoMutex.lock();
? ? if (_imageInfoQueue && !_imageInfoQueue->empty())
? ? {
? ? ? ? std::string fullpath = FileUtils::getInstance()->fullPathForFilename(filename);
? ? ? ? auto found = std::find_if(_imageInfoQueue->begin(),?_imageInfoQueue->end(), [&fullpath](ImageInfo* ptr)->bool{ return ptr->asyncStruct->filename == fullpath; });
? ? ? ? if (found != _imageInfoQueue->end())
? ? ? ? {
? ? ? ? ? ?(*found)->asyncStruct->callback = nullptr;
? ? ? ? }
? ? }
? ? _imageInfoMutex.unlock();
}
但是在極端情況下,調(diào)用 addImageAsync后馬上調(diào)用unbindImageAsync
此時(shí)loadImage線程還未將ImageInfo加入_imageInfoQueue
而是在unbindImageAsync之后添加,導(dǎo)致消息回調(diào)解綁失敗
無(wú)法調(diào)用(*found)->asyncStruct->callback = nullptr;
從而在異步加載完成后調(diào)用callback時(shí)候出現(xiàn)問(wèn)題
loadImage()線程
同時(shí)操作兩個(gè)鏈表
_asyncStructQueue
_imageInfoQueue
請(qǐng)求從_asyncStructQueue彈出后生成ImageInfo添加到_imageInfoQueue
void TextureCache::loadImage()
{
_asyncStructQueueMutex.lock();
_asyncStructQueue
_asyncStructQueueMutex.unlock();
.....臨界點(diǎn),執(zhí)行unbindImageAsync
_imageInfoMutex.lock();
?_imageInfoQueue->push_back(imageInfo);
_imageInfoMutex.unlock();
}
此處的Bug是兩個(gè)鏈表是分別加鎖的
請(qǐng)求從_asyncStructQueue彈出后,未即時(shí)插入_imageInfoQueue
在臨界點(diǎn)又調(diào)用了unbindImageAsync撤銷異步加載響應(yīng)
跟昨天提到的Bug類似,同樣會(huì)導(dǎo)致撤銷失敗
在回調(diào)時(shí)引起異常??
=================================================================
以下為轉(zhuǎn)帖
就以往的經(jīng)驗(yàn),異步加載圖片是一個(gè)復(fù)雜的工作,往往容易出現(xiàn)bug。?
????????那么,cocos2d-x提供的這個(gè)異步加載功能是否可靠?百度了一下,沒(méi)發(fā)現(xiàn)什么重要的信息,于是自己分析之。?
????????照cocos2d-x自身的注釋來(lái)看,這個(gè)addImageAsync函數(shù)是從0.8版本就有了,而現(xiàn)在是3.1版本,怎么也該穩(wěn)定了吧?可惜的是,里面的陷阱并不少。?
??????????
????????陷阱1:_textures未加鎖?
????????在異步加載時(shí),cocos2d-x主要用了兩個(gè)隊(duì)列,即_asyncStructQueue和_imageInfoQueue,在操作這兩個(gè)隊(duì)列的時(shí)候也都很小心的加鎖了。但是對(duì)_textures的訪問(wèn)則沒(méi)有加鎖。因此,如果先用addImageAsync進(jìn)行異步加載圖片A,再用addImage同步加載圖片B,則有幾率導(dǎo)致_textures這個(gè)對(duì)象被損壞,進(jìn)而導(dǎo)致程序不穩(wěn)定。?
????????修改:由于涉及到_textures的地方很多,逐一加鎖實(shí)在麻煩,所以干脆不加鎖,轉(zhuǎn)為讓異步線程不要訪問(wèn)_textures。大不了就是同一張圖片被多次加載,浪費(fèi)一些運(yùn)算量罷了。上層代碼小心控制的話,是不會(huì)真的有圖片被重復(fù)加載的。?
??????????
????????陷阱2:判斷邏輯錯(cuò)誤?
????????在cocos2d-x?3.1.1版本中,異步加載的代碼中有一句判斷:if(imageInfo->asyncStruct->filename.compare(asyncStruct->filename)),這是有問(wèn)題的。?
????????作者的本意可能是想,如果隊(duì)列中有多個(gè)請(qǐng)求都是加載同一幅圖片,那么其實(shí)只需要加載一次即可。可惜這個(gè)判斷寫(xiě)反了,下文又有一處判斷寫(xiě)反,導(dǎo)致不知所云。?
????????這個(gè)問(wèn)題在cocos2d-x?3.2版本已經(jīng)修復(fù)了。?
????????P.S.?字符串比較,還是用==、!=這樣的操作符比較好,可讀性和運(yùn)行性能都要優(yōu)于compare函數(shù)。?
????????修改:cocos官方已經(jīng)修正。不過(guò)其實(shí)跟陷阱1類似,不必判斷,大不了就是同一張圖片被多次加載,浪費(fèi)一些運(yùn)算量罷了。?
??????????
????????陷阱3:insert失敗導(dǎo)致內(nèi)存泄漏?
????????在異步加載完畢之后,主線程有一句:_textures.insert(?std::make_pair(filename,?texture)?);。?
????????由于陷阱1、陷阱2中,我們并沒(méi)有進(jìn)行徹底的檢查,所以有幾率出現(xiàn)重復(fù)加載的情形。(實(shí)際上,除非全程加鎖,否則很難徹底避免重復(fù)加載。然而,全程加鎖的話,異步加載也就沒(méi)有意義了)。當(dāng)出現(xiàn)重復(fù)加載同一張圖片時(shí),這里的insert就會(huì)失敗。于是,texture不會(huì)有被銷毀的機(jī)會(huì),于是造成內(nèi)存泄漏。?
??????????
????????陷阱4:創(chuàng)建線程時(shí),需要的變量尚未初始化完畢?
????????創(chuàng)建異步加載的線程時(shí),原始代碼是先創(chuàng)建線程,再設(shè)置_needQuit。?
????????正常應(yīng)該是先設(shè)置_needQuit為false(初始化值為true!),再創(chuàng)建線程。否則理論上有可能線程剛創(chuàng)建完畢就立即結(jié)束。?
??????????
????????疑似陷阱?
????????異步加載線程和主線程,都調(diào)用了Image::initWithImageFileThreadSafe,這個(gè)函數(shù)看名字似乎是線程安全的,實(shí)際上它調(diào)用了FileUtils::fullPathForFilename。這個(gè)函數(shù)除非參數(shù)是絕對(duì)路徑,否則就會(huì)對(duì)一個(gè)名為_(kāi)fullPathCache的哈希表進(jìn)行讀寫(xiě),若不加鎖就會(huì)出錯(cuò)。幸好在異步加載線程中,傳入給FileUtils::fullPathForFilename的參數(shù)已經(jīng)是絕對(duì)路徑,所以沒(méi)有上述的問(wèn)題。?
http://www.cocoachina.com/bbs/read.php?tid-312395.html
http://www.58player.com/blog-2479-108013.html
轉(zhuǎn)載于:https://my.oschina.net/robslove/blog/480514
總結(jié)
以上是生活随笔為你收集整理的cocos2dx-3 addImageAsync陷阱的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                        - 上一篇: CUG 数学进阶
 - 下一篇: 进行SEPM的灾难恢复时导入数据库后,S