1. 程式人生 > >【玩轉cocos2d-x之二十二】多執行緒和同步02-售票

【玩轉cocos2d-x之二十二】多執行緒和同步02-售票

pthread有很多不同應用,官網都有相應的API解釋和Sample,這裡不再重複,本文主要介紹一個cocos2d-x多執行緒和同步示例。

1.售票

孫鑫老師的C++和Java多執行緒售票一直讓我念念不忘,好吧,這裡用cocos2d-x和pthread實現一個吧。總共有100張火車票,有2個售票點A和B再售票,當票賣完了就結束了。我們知道當程式一開始程序就會建立一個主執行緒,所以可以在主執行緒基礎上再建立2個執行緒A和B,再執行緒A和B中分別售票,當票數為0的時候,結束執行緒A和B。

2.多執行緒售票

//TestLayer.h
class CTestLayer :
	public CCLayer
{
public:
	CTestLayer(void);
	~CTestLayer(void);

	CREATE_FUNC(CTestLayer);
	virtual bool init();

	pthread_t sellA_pid,sellB_pid;//執行緒id
	static int tickets;//票數

	static void* threadA(void* p);//執行緒A回撥
	static void* threadB(void* p);//執行緒B回撥
};


//TestLayer.cpp
#include "TestLayer.h"

int CTestLayer::tickets=100;//初始化票數100
CTestLayer::CTestLayer(void)
{
}


CTestLayer::~CTestLayer(void)
{
}

bool CTestLayer::init()
{
	bool bRet=false;
	do 
	{
		CC_BREAK_IF(!CCLayer::init());
		pthread_create(&sellA_pid,NULL,threadA,0);//建立執行緒A
		pthread_create(&sellB_pid,NULL,threadB,0);//建立執行緒B

		bRet=true;
	} while (0);
	return bRet;
}

void* CTestLayer::threadA(void* p)
{
	while(true)
	{
		if(tickets>0)
		{
			CCLog("A Sell %d",tickets--);//輸出售票,每次減1
		}
		else {
			break;
		}
	}
	return NULL;
}

void* CTestLayer::threadB(void* p)
{
	while(true)
	{
		if (tickets>0)
		{
			CCLog("B Sell %d",tickets--);
		}
		else 
		{
			break;
		}
	}
	return NULL;
}
程式碼很簡單,不多說了。我們來看一下輸出,會發現有很多不可思議的現象出現,因為每個人每次執行的結果都不一樣,所以這裡不貼結果了,不可思議的現象可能有:

(1)同一張票賣了2次。

(2)後面的票比前面的票先賣出去。

(3)第0張票竟然也可以賣。(這算站票麼。。。)

原因不多解釋了,時間片的問題,不明白的Google之。如果你覺得不會有這麼巧,那麼在列印結果前加上這麼一句:

Sleep(100);
人為干擾執行緒的執行,增大出錯機率。結果可能會是這樣:
A Sell 36
B Sell 36//賣2次了
A Sell 35
B Sell 34
A Sell 33
B Sell 32
A Sell 30//提早賣了
B Sell 31
B Sell 28
A Sell 29
A Sell 27
B Sell 26
A Sell 25
B Sell 24
A Sell 23
B Sell 22
A Sell 21
B Sell 20
A Sell 19
B Sell 18
A Sell 17
B Sell 16
A Sell 15
B Sell 14
A Sell 13
B Sell 12
A Sell 11
B Sell 10
A Sell 9
B Sell 8
A Sell 7
B Sell 6
A Sell 5
B Sell 4
A Sell 3
B Sell 2
A Sell 1
B Sell 0//站票。。。

3.利用互斥物件同步資料

這個問題主要是因為一個執行緒執行到一半的時候,時間片的切換導致另一個執行緒修改了同一個資料,當再次切換會原來執行緒並繼續往下執行的時候,資料由於被修改了導致結果出錯。所以我們要做的就是保證這個執行緒完全執行完,所以對執行緒加鎖是個不錯的注意,互斥物件mutex就是這個鎖。

3.1.初始化

在cpp外分配空間:

pthread_mutex_t CTestLayer::mutex;//mutex是靜態成員變數

在init中初始化,動態初始化:

pthread_mutex_init(&mutex,NULL);

3.2.加鎖和解鎖

以執行緒A為例:

void* CTestLayer::threadA(void* p)
{
	while(true)
	{
		pthread_mutex_lock(&mutex);//加鎖
		if(tickets>0)
		{
			Sleep(100);
			CCLog("A Sell %d",tickets--);
			pthread_mutex_unlock(&mutex);//解鎖
		}
		else {
			pthread_mutex_unlock(&mutex);//解鎖
			break;
		}
	}
	return NULL;
}

3.3.釋放

在當前層的解構函式中:

pthread_mutex_destroy(&mutex);//前提要保證是解鎖狀態,否則會返回16的錯誤,釋放失敗

這個時候再看一下結果,Bingo!

B Sell 16
A Sell 15
B Sell 14
B Sell 13
B Sell 12
B Sell 11
B Sell 10
B Sell 9
B Sell 8
B Sell 7
B Sell 6
B Sell 5
B Sell 4
B Sell 3
B Sell 2
B Sell 1

4.原始碼下載