テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

Unityでスレッドを使いつつUnityAPIを使う Spicy Pixel Concurrency Kit

今だとAsync/Awaitを使用したほうが良いです。







Unityは基本的にシングルスレッドで動く。非同期処理デザインで紹介しているコルーチンはシングルスレッドで動いてるし(Sleepをかけるとゲームが止まる)、その他の処理もマルチスレッド化している訳ではなさそうな印象。(描画部分は何かしているかもしれない。マルチコア云々の記述があったような気がするし)

一応マルチスレッド化して処理を並行に出来なくはないが、その場合は「Unityでスレッドを使う」にも書いた通り、別スレッドからUnityのAPIを使えないのが面倒くさい。なので、タスクシステムのような形でメインスレッドに処理を渡してやる必要がある。
(別スレッドで計算を処理し、メインスレッドで行うタスク一覧を発行、メインスレッドで処理する的な)

これ作るの楽しそうだけど面倒くさいなあと思ってたところ、同じような機能を持ったアセットがあった。

[Unity3D]Unity非同期処理デザインについて
http://tsubakit1.hateblo.jp/entry/20121013/1350063614
[Unity3D]Unityでスレッドを使う
http://tsubakit1.hateblo.jp/entry/20121110/1352555965



■Spicy Pixel
Spicy Pixelは、うまく説明できないが、Unity上でスレッドを便利にするアセットらしい。このアセットを利用することで、メインスレッド以外からUnityのAPIを呼べるようになるらしい。
最初は「APIを叩けるスレッドを作る」アセットかと思っていたのは秘密。

2014/02/02追記
(正しくは「別スレッドからメインスレッドで動作するキューを登録する」挙動だった気がする。
 なので、別スレッドでメソッドをコールしても即座に実行される訳ではない)

Spicy Pixel Concurrency Kit


上手く説明できないので、サンプルソース

	void Start () {
thread = new Thread(ThreadTest);
thread.Start();
}

public void ThreadTest()
{
GameObject.Instantiate(obj);
Thread.Sleep(5000);
}

まずコレは赤字のところで止まる。別スレッドではInstantiateが実行できないから。
で次はSpicy Pixelを使用した場合。

MonoBehaviourの代わりにConcurrentBehaviourを継承して、ソースコードを書き換える。

	void Start () {
thread = new Thread(ThreadTest);
thread.Start();
}
IEnumerator Createobject()
{
GameObject.Instantiate(obj);
yield return null;
}

public void ThreadTest()
{
taskFactory.StartNew(Createobject());
Thread.Sleep(5000);
}

これで、別スレッドで動作中のThreadTestからInstantiateが呼べるようになる。別スレッドなので当然Sleepで止まったりしないし、計算部分はマルチコアの性能を生かして頑張れるようになる。

うーん便利。



■処理を待ち合わせる

とは言え、上の例だと基本的に非同期で動作するので、Createobjectが終わる前にThread.Sleep(5000);が呼ばれちゃったりする。それが嫌な場合、例えばデータのダウンロードとかの処理を待ち合わせたい場合はWaitAllを使うらしい。

	Task t1 =taskFactory.StartNew(Createobject());
Task t2 = taskFactory.StartNew(Createobject());

//t1とt2が両方終わるまで待つ
Task.WaitAll(t1, t2);



マルチコアの性能を生かしたければ、こういった形の実装方法も覚えておいたほうが良いかもしれない。