as3でマルチスレッド的非同期処理を行なうには?
ActionScript3は残念ながらマルチスレッドではない。シングルスレッドだ。JavaのようにrunnableインターフェースやThreadクラスもなければ、Thread.yield()のようなものも見当たらない。
それでも多くの場合で何とかなる。しかし、大きい画像を読み込んだりする時など、非常に時間のかかる処理をする場合に困ることがある。
処理に時間がかかるのは仕方ない。そこで、その間に進捗状況を表示しようと考えるが、シングルスレッドではその処理に全CPUパワーを食われてしまって、画面の再描画が完全に止まってしまう。
UXを考えればこれは非常にまずい。ソフトウェアにおいて人が最もストレスを感じるのは、先が見えないことだからだ。
例えば、このような処理だ。trace出力は非常に高速に行われるものの、1000カウントするまで画面の描画は止まってしまう。
しかし、as3でもマルチスレッド的な動作を実現する方法はある。
一つの方法は、Event.ENTER_FRAMEを使う方法だ。この方法なら処理を続けながらも画面の再描画は行われる。同期バージョンに比べてカウントがかなり遅くなるので、最大カウントを1/10にしてある。
もうひとつの方法はTimerを使う方法だ。この方法でも画面の再描画は行われる。
このように、1サイクルで行なう処理をできるだけ短くして連続的に何度も処理を行なうことで擬似的なマルチスレッドが実現できる。
ただし、onEnterFrame()やonTimer()内で長い処理を行う場合は再描画がスムーズに行われない。
また、ユーザーの体感上の処理時間は非同期処理の方が短くなるだろうけれど、実際の処理時間は同期処理の方が短いことも注意しなければならない。ガマン出来ないほどの時間がかからないのであれば、非同期処理よりも同期処理の方が良い場合もあるだろう。
このような擬似マルチスレッドを実現するライブラリも存在する。
greenthreads - Project Hosting on Google Code
Thread - Spark project
しかし、どちらも専用のクラスをextendsする必要があるので、クラス設計によっては使用できない場合がある。そういった場合には、上のようなテクニックを使わざるを得ないだろう。
それでも多くの場合で何とかなる。しかし、大きい画像を読み込んだりする時など、非常に時間のかかる処理をする場合に困ることがある。
処理に時間がかかるのは仕方ない。そこで、その間に進捗状況を表示しようと考えるが、シングルスレッドではその処理に全CPUパワーを食われてしまって、画面の再描画が完全に止まってしまう。
UXを考えればこれは非常にまずい。ソフトウェアにおいて人が最もストレスを感じるのは、先が見えないことだからだ。
例えば、このような処理だ。trace出力は非常に高速に行われるものの、1000カウントするまで画面の描画は止まってしまう。
private function longTermProcessSync():void { var maxCount:int = 1000; for (var count:int = 0; count < maxCount; count++) { trace("longTermProcessSync() count=" + count); } }
しかし、as3でもマルチスレッド的な動作を実現する方法はある。
一つの方法は、Event.ENTER_FRAMEを使う方法だ。この方法なら処理を続けながらも画面の再描画は行われる。同期バージョンに比べてカウントがかなり遅くなるので、最大カウントを1/10にしてある。
private function longTermProcessAsyncEnterFrame():void { var maxCount:int = 100; var count:int = 0; addEventListener(Event.ENTER_FRAME, onEnterFrame); function onEnterFrame(evt:Event):void { count++; trace("longTermProcessAsyncEnterFrame() count=" + count); if (count >= maxCount) { removeEventListener(Event.ENTER_FRAME, onEnterFrame); } } }
もうひとつの方法はTimerを使う方法だ。この方法でも画面の再描画は行われる。
private function longTermProcessAsyncTimer():void { var maxCount:int = 100; var count:int = 0; var timer:Timer = new Timer(1); timer.addEventListener(TimerEvent.TIMER, onTimer); function onTimer(evt:TimerEvent):void { count++; trace("longTermProcessAsyncTimer() count=" + count); if (count >= maxCount) { timer.stop(); } } timer.start(); }
このように、1サイクルで行なう処理をできるだけ短くして連続的に何度も処理を行なうことで擬似的なマルチスレッドが実現できる。
ただし、onEnterFrame()やonTimer()内で長い処理を行う場合は再描画がスムーズに行われない。
また、ユーザーの体感上の処理時間は非同期処理の方が短くなるだろうけれど、実際の処理時間は同期処理の方が短いことも注意しなければならない。ガマン出来ないほどの時間がかからないのであれば、非同期処理よりも同期処理の方が良い場合もあるだろう。
このような擬似マルチスレッドを実現するライブラリも存在する。
greenthreads - Project Hosting on Google Code
Thread - Spark project
しかし、どちらも専用のクラスをextendsする必要があるので、クラス設計によっては使用できない場合がある。そういった場合には、上のようなテクニックを使わざるを得ないだろう。
コメント
コメントを投稿