【cocos2dx】マルチスレッドを利用した非同期処理

時間のかかる処理はユーザーにストレスを与えます。 できれば上手く工夫をして、重さを感じなくさせるよう工夫することが必要です。 今回は、そのような時に利用できる「マルチスレッド処理」についてcocos2dxで実装をしていきたいと思います。

マルチスレッドとは

プログラムの処理は通常「メインスレッド」で行われます。 コードの書かれた順番に処理を行いますので、例えば、凄く重い処理があれば、その処理が終わらないと次の処理へと移れません。 この部分で、オブジェクトの動きが止まったり、音楽が遅れたりする原因となります。

そこで用意されているのが「メインスレッド」とは別の「スレッド」で処理を行う方法です。 これが「マルチスレッド処理」と言われています。

Xcodeでのスレッドの確認方法

プロジェクトをビルドしている時に、[CPU]を調べると どの「スレッド」を利用しているかわかります。

2014-6-12-ookokkoo

通常の「メインスレッド」のみ。

2014-6-12-odokokkoo

「マルチスレッド」利用時。(Thread7がマルチスレッド)

2014-6-12-ookokko

マルチスレッドの注意点

マルチスレッドを使えば、非同期で処理が行えるので、画面の表示に時間がかかることは無くなります。 しかし、処理自体が軽くなることではないのでCPUに負担をかけます。 アプリが落ちたりする原因にもなるので、注意下さい。

マルチスレッドを実装する

今回は「cocos2dxのレシピ本」を参考にしています。 実践で利用する際には、以下の2点を考えて下さい。

  • メインスレッドで利用する変数
  • マルチスレッドでどのような処理を行うか

まずは、マルチスレッドの処理です。 今回は[POSIXスレッド]を利用しています。 [c]

include <semaphore.h>

include <unistd.h>

if CC_TARGET_PLATFORM == CC_PLATFORM_IOS

define MY_USE_NAMED_SEMAPHORE 1

else

define MY_USE_NAMED_SEMAPHORE 0

endif

if MY_USE_NAMED_SEMAPHORE

define MY_SEMAPHORE "cocosThreadSample.sem"

else

static sem_t s_semaphore;

endif

static sem_t* s_pSemaphore = NULL; static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t s_thread;

struct threadData{

//スレッド作業データ格納配列(__String型)
cocos2d::Vector<cocos2d::__String*> vec;

};

static threadData *s_threadData;

//マルチスレッド処理 void thread_function(void arg){

threadData *td = (threadData*)arg;


for (int i = 0; i < 20; i++) {

    cocos2d::__String *str = new cocos2d::__String();
    str->initWithFormat("%d",i);

    //0.5秒に一度データを投入
    pthread_mutex_lock(&s_mutex);
    td->vec.pushBack(str);
    pthread_mutex_unlock(&s_mutex);

    usleep(500000); // wait 0.5sec



}

sem_post(s_pSemaphore);
return NULL;

}

[/c]

以下が「メインスレッド」で利用する際の処理の一例です。 「マルチスレッド」で格納した数字をラベルで順番に表示をさせています。 [c] void MultiThreadLayer::startMultiThread(){

if MY_USE_NAMED_SEMAPHORE

s_pSemaphore = sem_open(MY_SEMAPHORE, O_CREAT,0644,0);
if (s_pSemaphore == SEM_FAILED) {

    return;

}

else

if (sem_init(&s_semaphore, 0, 0) < 0) {

    return;

}

s_pSemaphore = &s_semaphore;

endif

//スレッド用の作業データを作成
s_threadData = new threadData();

//マルチスレッド起動
pthread_mutex_init(&s_mutex, NULL);
pthread_create(&s_thread, NULL,thread_function, (void*)s_threadData);
pthread_detach(s_thread);

//スレッド表示ルート開始
//this->schedule(schedule_selector(MultiThreadLayer::update_therad_progress));

}

/*

メインスレッドのUpdate(ルート)

*/

void MultiThreadLayer::update_therad_progress(float delta){

std::string str = "";

//スレッドからデータを取得
pthread_mutex_lock(&s_mutex);
Vector<__String*> _ver  = s_threadData->vec;

for (int i =0; i < _ver.size(); i++) {

    //マルチスレッドで格納した__Stringを利用
    __String *ccstr = (__String*)_ver.at(i);
    str += ccstr->getCString();

}

pthread_mutex_unlock(&s_mutex);


//ラベル表示
auto _label = (Label*)this->getChildByTag(1);
const std::string _current = _label->getString();

if (strcmp(_current.c_str(), str.c_str()) != 0) {
    CCLOG("Update : %s",_current.c_str());
    _label->setString(str);

}


//マルチスレッド終了時の処理
int ret = sem_trywait(s_pSemaphore);
if (ret == 0) {

    //Updateを止める
    this->unschedule(schedule_selector(MultiThreadLayer::update_therad_progress));

    s_threadData->vec.clear(); // removeAllObject
    delete s_threadData;
    s_threadData = NULL;

if MY_NAMED_SEMAPHORE

    sem_close(s_pSemaphore);
    sem_unlink(MY_SEMAPHORE);

else

    sem_destroy(s_pSemaphore);

endif

}

} [/c]

最後に

cocos2dx3.xを利用しているのですが、iOS/Android共にマルチスレッドが実装できました。 ただ、実践でどのように利用するかが見えてきません。 よくあるのが「大量の画像を配置する」「大きいサイズの画像を配置する」など画像と連動して利用するのがいいのかもしれませんね。