Cocos2dx 3.0 過渡篇(二十六)C++11多執行緒std::thread的簡單使用
阿新 • • 發佈:2019-01-30
---------------
《上》
本篇介紹的是執行緒!在cocos2dx 2.0時代,我們使用的是pthread庫,是一套使用者級執行緒庫,被廣泛地使用在跨平臺應用上。但在cocos2dx 3.0中並未發現有pthread的支援檔案,原來c++11中已經擁有了一個更好用的用於執行緒操作的類std::thread。cocos2dx 3.0的版本預設是在vs2012版本,支援c++11的新特性,使用std::thread來建立執行緒簡直方便。
下面介紹下std::thread的一下簡單用法,程式碼需包含標頭檔案<thread>
執行結果如下圖:bool HelloWorld::init() { if ( !Layer::init() ) { return false; } std::thread t1(&HelloWorld::myThread,this);//建立一個分支執行緒,回撥到myThread函式裡 t1.join(); // t1.detach(); CCLOG("in major thread");//在主執行緒 return true; } void HelloWorld::myThread() { CCLOG("in my thread"); }
t.join()等待子執行緒myThread執行完之後,主執行緒才可以繼續執行下去,此時主執行緒會釋放掉執行完後的子執行緒資源。從上面的圖片也可以看出,是先輸出"in my thread",再輸出"in major thread"。
當然了,如果不想等待子執行緒,可以在主執行緒裡面執行t1.detach()將子執行緒從主執行緒裡分離,子執行緒執行完成後會自己釋放掉資源。分離後的執行緒,主執行緒將對它沒有控制權了。如下:
執行結果如下:std::thread t1(&HelloWorld::myThread,this);//建立一個分支執行緒,回撥到myThread函式裡 t1.detach();
當然了,也可以往執行緒函式裡穿引數,這裡用到了bind。下面例子在例項化執行緒物件的時候,線上程函式myThread後面緊接著傳入兩個引數。
輸出結果如下圖:bool HelloWorld::init() { if ( !Layer::init() ) { return false; } std::thread t1(&HelloWorld::myThread,this,10,20);//建立一個分支執行緒,回撥到myThread函式裡 t1.join(); // t1.detach(); CCLOG("in major thread");//在主執行緒 return true; } void HelloWorld::myThread(int first,int second) { CCLOG("in my thread,first = %d,second = %d",first,second); }
恩,這篇先講到這裡吧。下篇講下互斥量,最後以一個抄襲的例子瀟灑的結束掉執行緒的一生
尊重原創,轉載請註明來源:
《下》簡單的東西我都說的差不多了,想挖點深的差點把自己給填進去。下面實際演練一下。請允許我參考偶爾E往事的一篇執行緒的部落格, 他用的是pThread,這裡我就用std::thread。
1.售票
孫鑫老師的C++和Java多執行緒售票也一直讓我念念不忘(好吧,我承認我沒看過),這裡用cocos2d-x3.0和C++11的std::thread實現一個吧。總共有100張諾亞方舟船票,有2個售票點A和B在售票(一張票就一百億美元吧),當票賣完了就結束了。我們知道當程式一開始程序就會建立一個主執行緒,所以可以在主執行緒基礎上再建立2個執行緒A和B,再執行緒A和B中分別售票,當票數為0的時候,結束執行緒A和B。
2.多執行緒售票,程式碼如下:
//HelloWorld.h
class HelloWorld : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(HelloWorld);
void myThreadA();//執行緒A
void myThreadB();//執行緒B
int tickets;//票數
};
//.cpp
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
tickets = 100;//100張票
std::thread tA(&HelloWorld::myThreadA,this);//建立一個分支執行緒,回撥到myThread函式裡
std::thread tB(&HelloWorld::myThreadB,this);
tA.detach();
tB.detach();
// t1.detach();
CCLOG("in major thread");//在主執行緒
return true;
}
void HelloWorld::myThreadA()
{
while(true)
{
if(tickets>0)
{
Sleep(10);
CCLOG("A Sell %d",tickets--);//輸出售票,每次減1
}
else {
break;
}
}
}
void HelloWorld::myThreadB()
{
while(true)
{
if (tickets>0)
{
Sleep(10);
CCLOG("B Sell %d",tickets--);
}
else
{
break;
}
}
}
程式碼很簡單,不多說了。我們來看一下輸出,會發現有很多喜聞樂見的現象出現,因為每個人每次執行的結果都不一樣,所以這裡不貼結果了,其中比較有意思的現象是同一張票賣了兩次?!
原因不多解釋了,時間片的問題,不明白的Google之。如果你覺得不會有這麼巧,那麼在列印結果前加上這麼一句:
Sleep(100);
執行結果如圖所示:
3.利用互斥物件同步資料
這個問題主要是因為一個執行緒執行到一半的時候,時間片的切換導致另一個執行緒修改了同一個資料,當再次切換會原來執行緒並繼續往下執行的時候,資料由於被修改了導致結果出錯。所以我們要做的就是保證這個執行緒完全執行完,所以對執行緒加鎖是個不錯的注意,互斥物件mutex就是這個鎖。
3.1、初始化互斥鎖
std::mutex mutex;//執行緒互斥物件
3.2、修改myThreadA與myThreadB的程式碼,在裡面新增互斥鎖
void HelloWorld::myThreadA()
{
while(true)
{
mutex.lock();//加鎖
if(tickets>0)
{
Sleep(10);
CCLOG("A Sell %d",tickets--);//輸出售票,每次減1
mutex.unlock();//解鎖
}
else {
mutex.unlock();
break;
}
}
}
void HelloWorld::myThreadB()
{
while(true)
{
mutex.lock();
if (tickets>0)
{
Sleep(10);
CCLOG("B Sell %d",tickets--);
mutex.unlock();
}
else
{
mutex.unlock();
break;
}
}
}
執行結果如下,完美使用std::mutex有一個要注意的地方:線上程A中std::mutex使用成員函式lock加鎖unlock解鎖,看起來工作的很好,但這樣是不安全的,你得始終記住lock之後一定要unlock,但是如果在它們中間出現了異常或者執行緒直接退出了unlock就沒有執行,因為這個互斥量是獨佔式的,所以在threadA沒有解鎖之前,其他使用這個互斥量加鎖的執行緒會一直處於等待狀態得不到執行
恩,就寫到這裡。嘿嘿嘿嘿。