as3でマルチスレッド的非同期処理を行なうには?

ActionScript3は残念ながらマルチスレッドではない。シングルスレッドだ。JavaのようにrunnableインターフェースやThreadクラスもなければ、Thread.yield()のようなものも見当たらない。

それでも多くの場合で何とかなる。しかし、大きい画像を読み込んだりする時など、非常に時間のかかる処理をする場合に困ることがある。

処理に時間がかかるのは仕方ない。そこで、その間に進捗状況を表示しようと考えるが、シングルスレッドではその処理に全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する必要があるので、クラス設計によっては使用できない場合がある。そういった場合には、上のようなテクニックを使わざるを得ないだろう。

コメント

このブログの人気の投稿

レオナルド・ダ・ビンチはなぜノートを「鏡文字」で書いたのか?

macでsmb(samba)共有サーバーに別名で接続(別アカウント名で接続)する方法

Google DriveにCURLでアップロードするには?