#include "ThreadTestScene.h"
#include <pthread.h> // pthread 사용
#include <unistd.h> // sleep() 사용
CCScene* ThreadTest::scene()
{
CCScene* scene = CCScene::node();
ThreadTest* layer = ThreadTest::node();
scene->addChild(layer);
return scene;
}
ThreadTest::ThreadTest()
{
}
ThreadTest::~ThreadTest()
{
m_pLabel->release();
if ( m_pThread0 )
{
pthread_detach(m_pThread0);
m_pThread0 = NULL;
}
if ( m_pThread1 )
{
pthread_detach(m_pThread1);
m_pThread1 = NULL;
}
}
bool ThreadTest::init()
{
bool bRet = false;
{
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
CCLabelTTF* label = CCLabelTTF::labelWithString("Thread Test", "Arial", 28);
addChild(label, 0);
label->setPosition( ccp(winSize.width/2, winSize.height-50) );
setIsTouchEnabled(true);
// create a label to display the tip string
m_pLabel = CCLabelTTF::labelWithString("Touch the screen to connect", "Arial", 22);
m_pLabel->setPosition(ccp(winSize.width / 2, winSize.height / 2));
addChild(m_pLabel, 0);
m_pLabel->retain();
CCMenuItemImage *pCloseItem = CCMenuItemImage::itemFromNormalImage(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(ThreadTest::menuCallback) );
pCloseItem->setPosition( ccp(winSize.width - 20, 20) );
// create menu, it's an autorelease object
CCMenu* pMenu = CCMenu::menuWithItems(pCloseItem, NULL);
pMenu->setPosition( CCPointZero );
this->addChild(pMenu, 1);
bRet = true;
}
return bRet;
}
// 뮤텍스 객체 선언
int flag = 0;
int MAX_ROOP = 5; // "thread를 몇 번 실행할 것인가?"를 정하는 값이다.
pthread_mutex_t mutex_lock;
pthread_cond_t thread_cond_write = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t thread_cond_read = PTHREAD_COND_INITIALIZER;
// 뮤텍스로 보호할 정보와 객체
struct CharaterParameter
{
int hp;
int mp;
};
struct CharaterParameter charaterParameter;
void* do_write(void* data)
{
charaterParameter.hp = 0;
charaterParameter.mp = 0;
CCLog("Write:: this scope is working once because it is not in While loop \n");
while (1)
{
CCLog("pthread Write");
if(flag >= MAX_ROOP )
{
/* thread를 중지하지 않으면 Cocos2d-x 프로그램이 종료되지 않는다.
강제 종료를 하여도 thread는 계속 돌아간다.
따라서 pThread_exit()로 종료해줘야 한다.
*/
pthread_exit(NULL);
}
/* pthread_cond_signal는 signal을 보내는 역활이다.
쓰레드간의 통신을 위해서 사용한다.
쓰레드 A는 pthread_cond_signal을 사용하며, 쓰레드 B에는 pthread_cond_wait를 사용한다고 가정하자.
쓰레드 A의 pthread_cond_signal에서 보낸 signal은 쓰레드 B의 pthread_cond_wait로 signal을 받아야 한다.
pthread_cond_wait는 계속 pthread_cond_signal에서 signal을 받아야 쓰레드를 작동시킨다.
쓰레드 A의 signal을 받지 못하면 계속 대기하기 때문에, 쓰레드 B는 작동하지 않는다.
*/
pthread_mutex_lock(&mutex_lock);
pthread_cond_signal(&thread_cond_write);
CCLog("pthread_cond_signal: thread_cond_write");
charaterParameter.hp = random() % 6000;
charaterParameter.mp = random() % 6000;
if( flag > 0)
{
pthread_cond_wait(&thread_cond_read, &mutex_lock);
CCLog("pthread_cond_wait : thread_cond_read");
}
else
{
CCLog("pthread_cond_wait : thread_cond_read is work after flag value over 1");
}
pthread_mutex_unlock(&mutex_lock);
sleep(1);
}
CCLog("pthread function is never working after While loop finished");
return NULL;
}
void* do_read(void* data)
{
CCLog("Read:: this scope is working once because it is not in While loop \n");
while (1)
{
CCLog("pthread Read");
if(flag >= MAX_ROOP )
{
pthread_exit(NULL);
}
pthread_mutex_lock(&mutex_lock);
pthread_cond_wait(&thread_cond_write, &mutex_lock);
CCLog("pthread_cond_wait : thread_cond_write");
CCLog("thread %4d :: %4d + %4d = %6d", flag, charaterParameter.hp, charaterParameter.mp, charaterParameter.hp + charaterParameter.mp);
flag++;
pthread_cond_signal(&thread_cond_read);
CCLog("pthread_cond_signal : thread_cond_read\n");
pthread_mutex_unlock(&mutex_lock);
//sleep(1);
}
return NULL;
}
void ThreadTest::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{
int a = 1;
int b = 2;
int thread_id[2] = {0};
CCLog("pthread 초기화 작업 시작\n");
flag = 0; // flag 초기화
pthread_mutex_init(&mutex_lock, NULL);
pthread_cond_init(&thread_cond_write, NULL);
pthread_cond_init(&thread_cond_read, NULL);
thread_id[0] = pthread_create(&m_pThread0, NULL, do_write, (void *)&a);
thread_id[1] = pthread_create(&m_pThread1, NULL, do_read, (void *)&b);
CCLog("pthread 초기화 작업 끝\n");
if( thread_id[0] < 0 || thread_id[1] < 0 )
{
CCLog("pThread Error");
}
else
{
/* pthread_join은 모든 쓰레드가 다 종료할 때까지 기다린다.
왜냐하면 쓰레드 실행 도중에 부모 쓰레드가 종료되면 안 되기 때문이다.
쓰레드가 모든 일을 끝내면 pthread_join을 깨우게 된다.
그렇게 되면 쓰레드가 가지고 있는 모든 자원을 되돌려준다.
만일 실행되고 있는 쓰레드를 즉시 중지하기 원한다면 pthread_join을 쓰지 않고
pthread_cancel()과 pthread_terstcancel을 쓰면 된다.
*/
int status;
CCLog("pthread 종료 작업 시작\n");
pthread_join(m_pThread0, (void **)&status);
pthread_join(m_pThread1, (void **)&status);
pthread_mutex_destroy(&mutex_lock);
pthread_cond_destroy(&thread_cond_write);
pthread_cond_destroy(&thread_cond_read);
CCLog("thread finished and status is %d", status);
m_pThread0 = NULL;
m_pThread1 = NULL;
CCLog("pthread 종료 작업 끝\n");
}
}
// 메뉴를 선택한다.
void ThreadTest::menuCallback(CCObject* pSender)
{
CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}