多線程併發訪問在Cocos2d-x引擎中用的不是不少,這主要是由於中整個結構設計沒有采用多線程。源自於Objective-C的Ref對象,須要使用AutoreleasePool進行內存管理,AutoreleasePool是非線程安全的,全部不推薦在子多線程中調用Ref對象的retain()、 release()和autorelease()等函數。另外,OpenGL上下文對象也是不支持線程安全的。
可是有的時候咱們須要異步加載一些資源,例如:加載圖片紋理、聲音的預處理和網絡請求數據等。若是是異步加載圖片紋理咱們可使用第20.4.4一節介紹的內容。但聲音的預處理和網絡請求數據等就須要本身經過多線程技術實現了。
Cocos2d-x引擎也提供了多線程技術,Cocos2d-x 3.x以前是使用第三方的pthread技術。Cocos2d-x 3.x以後使用C++11新規範中的std::thread多線程技術,std::thread使用起來比較簡單。
1.std::thread多線程技術
std::thread是C++11 引入了一個新的線程庫,它提供了線程管理相關函數,std::thread庫中還提供了std::mutex(互斥量),經過std::mutex能夠實現線程同步。
啓動一個新的線程很是簡單,當咱們建立一個 std::thread 對象時候,它便會自行啓動。建立線程std::thread 對象時,能夠提供該線程的回調函數。下面代碼實現了建立線程和線程函數的回調:
html
[html] view plaincopyios
#include <thread> 安全
#include <iostream> 微信
void callfn(){ ① 網絡
std::cout << "Hello thread! " << std::endl; 多線程
} 併發
int main(){ app
std::thread t1(callfn); ② 異步
t1.join(); ③ 函數
return 0;
}
上述代碼第②行是建立t1線程對象,它的參數是函數指針callfn,若是須要,咱們還能夠爲回調函數提供參數。代碼第①行是回調函數的定義。第③行代碼t1.join()是將子線程與主線程合併,這種合併可以使子線程執行完成後才能繼續執行主線程,這是爲了不子線程還在執行,主線程已經執行結束而撤銷。
建立線程還可使用堆的方式分配內存,代碼以下:
[html] view plaincopy
void callfn(){
std::cout << "Hello thread! " << std::endl;
}
int main(){
std::thread* t1 = new std::thread(callfn); ①
t1->join();
delete t1; ②
t1 = nullptr; ③
return 0;
}
上述代碼第①行是經過堆方式分配內存,即經過new運算符建立動態線程對象。所以須要在使用完成的狀況下釋放對象,咱們在代碼第②行使用delete t1語句釋放,釋放完成還以經過代碼第③行t1 = nullptr設置指針變量,這樣能夠防止「野指針」。
2.異步預處理聲音
std::thread線程Cocos2d-x中有不少現實的應用,異步預處理聲音,異步加載一些資源資源文件,異步加載圖片紋理Cocos2d-x爲咱們提供了API,可是它們異步加載須要咱們本身實現。下面咱們介紹一下異步預處理聲音。
咱們在前面20.5一節介紹了聲音預處理和清除,在那一節中預處理聲音是同步的,它會致使堵塞主線程,使用戶的感受會「卡」了一下。若是這個「卡」比較長,咱們解決主線程阻塞問題,改善用戶體驗,咱們能夠異步預處理聲音。
咱們在20.5一節的案例中採用std::thread線程異步預處理聲音,咱們能夠在AppDelegate中進行異步加載,修改以後的AppDelegate.h代碼以下:
#include "cocos2d.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
class AppDelegate : private cocos2d::Application
{
private:
std::thread *_loadingAudioThread; ①
void loadingAudio(); ②
public:
AppDelegate();
virtual ~AppDelegate();
… …
};
咱們在第①行聲明瞭私有的std::thread線程指針變量_loadingAudioThread。第②代碼是聲明瞭私有的異步預處理聲音函數loadingAudio()。
修改以後的AppDelegate.cpp代碼以下:
[html] view plaincopy
include "AppDelegate.h"
#include "HelloWorldScene.h"
USING_NS_CC;
AppDelegate::AppDelegate()
{
_loadingAudioThread = new std::thread(&AppDelegate::loadingAudio,this); ①
}
AppDelegate::~AppDelegate()
{
_loadingAudioThread->join(); ②
CC_SAFE_DELETE(_loadingAudioThread); ③
}
bool AppDelegate::applicationDidFinishLaunching() {
… …
return true;
}
void AppDelegate::applicationDidEnterBackground() {
Director::getInstance()->stopAnimation();
SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}
void AppDelegate::applicationWillEnterForeground() {
Director::getInstance()->startAnimation();
SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}
void AppDelegate::loadingAudio() ④
{
//初始化 音樂
SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/Jazz.mp3");
SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/Synth.mp3");
//初始化 音效
SimpleAudioEngine::getInstance()->preloadEffect("sound/Blip.wav");
}
上述代碼第①行是在構造函數裏建立線程對象,建立線程對象代碼也能夠放置到 AppDelegate::applicationDidFinishLaunching()函數中,咱們根據須要在合適的地方建立。
第②行代碼_loadingAudioThread->join()是合併線程到主線程,這個處理是在析構函數中調用的,join()函數通常是在線程處理完成後調用,咱們能夠在析構函數中調用,也能夠在一些退出函數(如Layer的onExit函數)中調用。因爲是_loadingAudioThread動態對象指針類型,須要釋放對象,咱們能夠經過第③行代碼CC_SAFE_DELETE(_loadingAudioThread)釋放。CC_SAFE_DELETE宏的做用以下:
delete _loadingAudioThread;
_loadingAudioThread = nullptr;
第④行代碼AppDelegate::loadingAudio() 定義了線程回調函數,咱們在這個函數中預處理聲音。
更多內容請關注國內第一本Cocos2d-x 3.2版本圖書《Cocos2d-x實戰:C++卷》
本書交流討論網站:http://www.cocoagame.net
更多精彩視頻課程請關注智捷課堂Cocos課程:http://v.51work6.com
歡迎加入Cocos2d-x技術討論羣:257760386
歡迎關注智捷iOS課堂微信公共平臺