ActionScript のマルチスレッド機能についての情報

Flash Player 11.3 が公開されたばかりですが、最新の Flash ランタイムのロードマップによれば、次のバージョンの Dolores から、いよいよマルチスレッド機能を ActionScript から利用できるということです。

複数のスレッドが使えると、スクリプトの実行を、メインスレッドとは別のスレッドに割り当てることができます。そうすると、今までは画面をフリーズさせる原因になっていたような重たい処理も、描画処理と並行して実行できるようになります。 (コアが複数あればですが)

この待望の (?) マルチスレッドに関する新しい API の情報が、製品担当者のブログに公開されていましたので、今回はその内容をご紹介します。 (A sneak peek: Concurrency with ActionScript Workers

といっても、あまりはっきり書かれていない箇所も多いため、主な箇所のみ要約します。ともあれ、昨年の MAX で話された内容からは大きく変わっている印象です。

Worker クラス

Flash ランタイムにおけるスレッドの基本機能は Worker が提供します。その名前から、Flash でも、HTML5 の Web Worker と同じコンセプトを採用したかったことが伺えます。 (全く新しい考えを導入するよりは、良い判断な気がします)

以下、この記事内では、Flash ランタイムが SWF 実行のため最初に作るスレッドを UI Worker と呼ぶことにします (メインタイムラインが実行されるスレッドです)。

さて、その UI Worker から、子の Worker を作るには、以下のようなコードを書きます。

var worker:Worker;
woker = WorkerDomain.current.createWorker(Workers.EncoderWorker);
worker.start(); 

WorkderDomain.current が何を指すのかは不明です (Worker のインスタンスだとすると下のコードとの違いに説明がつかないので) が、とりあえず、createWorker() メソッドで、Worker のインスタンスが生成できます。

createWorker() の引数には、ByteArray に SWF ファイルを読み込んだもの、もしくは、Embed タグを使って埋め込まれた SWF ファイル、のどちらかを渡せば良いようです。

生成された Worker は、渡された SWF を使って初期化されます。子 Woker からは、マウスやカメラの操作などは別として、ほとんどの API が利用できるようです。

作成された Worker のインスタンスに対して start() メソッドを呼ぶと、スレッドの実行が開始されます。

Worker 間の通信

スレッド間の通信を行いたい場合は、MessageChannel を使うということです。以下のようにして、インスタンスを生成します。

var mainToBack:MessageChannel;
mainToBack = Worker.current.createMessageChannel(worker);

creteMessateChannel() メソッドの引数に、メッセージを送りたい Worker を指定すると、その Worker との通信専用の MessageChannel のインスタンスが作成されます。上のコード内の Worker..current が指すのは UI Worker、という理解で良いようです。

ところで、もし、上のコードが UI Worker 内で実行されたとすると、その時点で、mainToBack への参照を持っているのは UI Worker だけです。子の Worker と mainToBack を使って通信するには、子 Worker にも mainToBack への参照を渡す必要があります。

そのため、Worker には、他の Worker と属性を共有する機能がついています。この機能を使って mainToBack への参照を、UI Worker から子 Worker に渡す場合は、以下のようなコードになります。

// UI Workerから、子Workerに属性名と値を指定
worker.setSharedProperty("mainToBack", mainToBack);   

子 Worker 側では、以下のようにしてデータを受け取ります。上のコードを見ると、自身の属性に値が設定された様な印象ですが、下のコードは、UI Worker の属性を取り出す、という印象です。

// UI Workerから属性名を指定して値を取得
mainToBack = Worker.current.getSharedProperty("mainToBack");   

以上で、mainToBack への参照が、子 Worker と共有されました。ここからは、mainToBack を使って、Worker 間でデータの送受信ができます。なお、上の手順は、MessageChannel 以外にも、Worker の初期化に必要なデータを送る目的での利用が可能です。

MessageChannel を使ったデータの送受信には、send() と receive() メソッドを使います。引数には AMF3 フォーマットに変換できる型が指定できるようです。ということで、データはコピーされてから AMF3 形式に変換されて (一部例外あり) 相手の Worker に送られる、という話のようです。

メッセージの送り手は以下の様に記述します。

mainToBack.send(100);   

受け手側は以下のようにしてデータを受け取ります。

var value:uint = mainToBack.receive();   

MessageChannel オブジェクトにメッセージが届くと、Event.CHANNEL_MESSAGE というイベントが発生します。そのため、上のコードは、通常、そのイベントハンドラ内に書くことになると思われます。

receive() メソッドには、引数で同期/非同期を指定できることになっています。デフォルトでは同期、すなわち値を受け取るまで receive() は待ち続ける (次の行は実行されない) という動作だそうです。

MessageChannel には、メッセージの有無を知らせる messageAvailable 属性があります。これを使って、以下のような書き方もできそうです。

if (mainToBack.messageAvailable)
  mainToBack.receive();

MessageChannel では、データ送信ごとにデータのコピーが発生するため、大量のデータの受け渡しには効率が悪そうです。そこで、別のデータ共有方法が提供されるようです。

ByteArray の共有

Worker 機能の追加に合わせて、Worker 同士が ByteArray のインスタンスを共有できるよう、新しく ByteArray に shareble という属性が追加されるようです。

MessageChannel は相手にデータを渡す、という動作でしたが、共有 ByteArray は、Worker 間で、同じ ByteArray インスタンス、すなわちメモリ領域を参照するという動作になります。そのため、データのコピーが発生しません。

この点から、共有 ByteArray の方が効率の良いデータ共有手段だと考えられます。最初に ByteArray のインスタンスへの参照を共有する必要があるため、結局 MessageChannel などを使用することにはなりますが。

ここで、例えば、sharedMemory という名前で ByteArray インスタンスが共有されている状態を考えてみます。そして、片方の Worker は以下のようにデータを更新します。

sharedMemory.position = 0;
sharedMemory.writeUnsignedInt(100);

すると、反対側の Worker は、以下のようにして更新された値を読み取れます。

sharedMemory.position = 0;
var value:uint = sharedMemory.readUnsignedInt();

上のコードでは、2 つの Worker は同じメモリアドレスを参照していることが分かるでしょうか。

また、共有 ByteArray を使った BitmapData の共有を容易にするため、BitmapData に copyPixelsToByteArray() という API が追加されるようです。使い方は以下のとおりです。

myBitmapData.copyPixelsToByteArray(myBitmapData.rect, sharedMemory);   

これにより、一時バッファを作成すること無く、BitmapData から ByteArray にデータを移動できるようになりそうです。

データの競合について

ByteArray を使用する場合、複数の Worker が、同時に同じデータを操作する可能性があります。読み取り専用のデータであれば問題無いかもしれませんが、書き込み処理がある場合は、正しい順序で更新が行われるように交通整理が必要です。

そのため、flash.concurrent というパッケージに Mutex と Condition という新しいクラスが追加されるようです。

共有 ByteArray は効率が良い代わりに、使い方がちょっと面倒になる場合がある、ということのようです。

 

トラックバック(0)

トラックバックURL: http://cuaoar.jp/mt4/mt-tb.cgi/321

コメント(2)

マルチスレッドは必ずしもイコールマルチコアではないと思うのですが、ActionScriptは違うのですか?
マルチコアなら別々のコアで実行されますが、そうでないときは同じコアで時間分割で擬似的な並列処理になるはずです。
半分の時間で終わらないという意味では本当のマルチスレッドではないと考えられるのでしょうが、メインスレッドがフリーズしないというメリットが残るのでは?

Laurent FABRE さん、こんにちは。

括弧内は、逆に、擬似並列処理じゃないか?というつっこみが来るかと思って付け足したものです。
確かに "並行して実行" 自体を否定しているように読めますね。分かり難くてすみません。

コメントする

2014年1月

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
レンタルサーバー

月別 アーカイブ

Powered by Movable Type 4.261